All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.microsoft.azure.storage.table.TableQuery Maven / Gradle / Ivy

/**
 * Copyright Microsoft Corporation
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.microsoft.azure.storage.table;

import java.util.Date;
import java.util.Formatter;
import java.util.UUID;

import com.microsoft.azure.storage.Constants;
import com.microsoft.azure.storage.StorageException;
import com.microsoft.azure.storage.core.SR;
import com.microsoft.azure.storage.core.UriQueryBuilder;
import com.microsoft.azure.storage.core.Utility;

/**
 * A class which represents a query against a specified table. A {@link TableQuery} instance aggregates the query
 * parameters to use when the query is executed. One of the execute or executeSegmented
 * methods of {@link CloudTableClient} must be called to execute the query. The parameters are encoded and passed to the
 * server when the table query is executed.
 * 

* To create a table query with fluent syntax, the {@link #from} static factory method and the {@link #where}, * {@link #select}, and {@link #take} mutator methods each return a reference to the object which can be chained into a * single expression. Use the {@link #from(Class)} static class factory method to create a * TableQuery instance that executes on the named table with entities of the specified {@link TableEntity} * implementing type. Use the {@link #where} method to specify a filter expression for the entities returned. Use the * {@link #select} method to specify the table entity properties to return. Use the {@link #take} method to limit the * number of entities returned by the query. Note that nothing prevents calling these methods more than once on a * TableQuery, so the values saved in the TableQuery will be the last encountered in order of * execution. *

* As an example, you could construct a table query using fluent syntax: *

* TableQuery<TableServiceEntity> myQuery = TableQuery.from("Products", DynamicTableEntity.class)
*     .where("(PartitionKey eq 'ProductsMNO') and (RowKey ge 'Napkin')")
*     .take(25)
*     .select(new String[] {"InventoryCount"});
*

* This example creates a query on the "Products" table for all entities where the PartitionKey value is "ProductsMNO" * and the RowKey value is greater than or equal to "Napkin" and requests the first 25 matching entities, selecting only * the common properties and the property named "InventoryCount", and returns them as {@link DynamicTableEntity} * objects. *

* Filter expressions for use with the {@link #where} method or {@link #setFilterString} method can be created using * fluent syntax with the overloaded {@link #generateFilterCondition} methods and {@link #combineFilters} method, using * the comparison operators defined in {@link QueryComparisons} and the logical operators defined in {@link Operators}. * Note that the first operand in a filter comparison must be a property name, and the second operand must evaluate to a * constant. The PartitionKey and RowKey property values are String types for comparison purposes. *

* The values that may be used in table queries are explained in more detail in the MSDN topic Querying Tables and Entities, but note that * the space characters within values do not need to be URL-encoded, as this will be done when the query is executed. *

* The {@link TableQuery#TableQuery(Class)} constructor and {@link TableQuery#from(Class)} static factory methods * require a class type which implements {@link TableEntity} and contains a nullary constructor. If the query will be * executed using an {@link EntityResolver}, the caller may specify {@link TableServiceEntity} .class as * the class type. * * @param * A class type which implements {@link TableEntity} and contains a nullary constructor. Note: when using an * inner class to define the class type, mark the class as static. */ public class TableQuery { /** * A static class that maps identifiers to filter expression operators. */ public static class Operators { /** * And */ public static final String AND = "and"; /** * Not */ public static final String NOT = "not"; /** * Or */ public static final String OR = "or"; } /** * A static class that maps identifiers to filter property comparison operators. */ public static class QueryComparisons { /** * Equal */ public static final String EQUAL = "eq"; /** * Not Equal */ public static final String NOT_EQUAL = "ne"; /** * Greater Than */ public static final String GREATER_THAN = "gt"; /** * Greater Than Or Equal */ public static final String GREATER_THAN_OR_EQUAL = "ge"; /** * Less Than */ public static final String LESS_THAN = "lt"; /** * Less Than Or Equal */ public static final String LESS_THAN_OR_EQUAL = "le"; } /** * A static factory method that constructs a {@link TableQuery} instance and defines its table entity type. The * method returns the {@link TableQuery} instance reference, allowing additional methods to be chained to modify the * query. *

* The created {@link TableQuery} instance is specialized for table entities of the specified class type T. Callers * may specify {@link TableServiceEntity} .class as the class type parameter if no more specialized * type is required. * * @param clazzType * The java.lang.Class of the class T implementing the {@link TableEntity} * interface that represents the table entity type for the query. * * @return * The {@link TableQuery} instance with the entity type specialization set. * */ public static TableQuery from(final Class clazzType) { return new TableQuery(clazzType); } /** * Generates a property filter condition string for a boolean value. Creates a formatted string to use * in a filter expression that uses the specified operation to compare the property with the value, formatted as a * boolean, as in the following example: *

* String condition = generateFilterCondition("BooleanProperty", QueryComparisons.EQUAL, false); *

* This statement sets condition to the following value: *

* BooleanProperty eq false * * @param propertyName * A String which specifies the name of the property to compare. * @param operation * A String which specifies the comparison operator to use. * @param value * A boolean which specifies the value to compare with the property. * @return * A String which represents the formatted filter condition. */ public static String generateFilterCondition(String propertyName, String operation, final boolean value) { return generateFilterCondition(propertyName, operation, value ? Constants.TRUE : Constants.FALSE, EdmType.BOOLEAN); } /** * Generates a property filter condition string for a byte[] value. Creates a formatted string to use * in a filter expression that uses the specified operation to compare the property with the value, formatted as a * binary value, as in the following example: *

* String condition = generateFilterCondition("ByteArray", QueryComparisons.EQUAL, new byte[] {0x01, 0x0f}); *

* This statement sets condition to the following value: *

* ByteArray eq X'010f' * * @param propertyName * A String which specifies the name of the property to compare. * @param operation * A String which specifies the comparison operator to use. * @param value * A byte array which specifies the value to compare with the property. * @return * A String which represents the formatted filter condition. */ public static String generateFilterCondition(String propertyName, String operation, final byte[] value) { StringBuilder sb = new StringBuilder(); Formatter formatter = new Formatter(sb); for (byte b : value) { formatter.format("%02x", b); } formatter.flush(); formatter.close(); return generateFilterCondition(propertyName, operation, sb.toString(), EdmType.BINARY); } /** * Generates a property filter condition string for a Byte[] value. Creates a formatted string to use * in a filter expression that uses the specified operation to compare the property with the value, formatted as a * binary value, as in the following example: *

* String condition = generateFilterCondition("ByteArray", QueryComparisons.EQUAL, new Byte[] {0x01, 0xfe}); *

* This statement sets condition to the following value: *

* ByteArray eq X'01fe' * * @param propertyName * A String which specifies the name of the property to compare. * @param operation * A String which specifies the comparison operator to use. * @param value * A Byte array which specifies the value to compare with the property. * @return * A String which represents the formatted filter condition. */ public static String generateFilterCondition(String propertyName, String operation, final Byte[] value) { StringBuilder sb = new StringBuilder(); Formatter formatter = new Formatter(sb); for (byte b : value) { formatter.format("%02x", b); } formatter.flush(); formatter.close(); return generateFilterCondition(propertyName, operation, sb.toString(), EdmType.BINARY); } /** * Generates a property filter condition string for a java.util.Date value. Creates a formatted string to use in * a filter expression that uses the specified operation to compare the property with the value, formatted as a * datetime value, as in the following example: *

* String condition = generateFilterCondition("FutureDate", QueryComparisons.GREATER_THAN, new Date()); *

* This statement sets condition to something like the following value: *

* FutureDate gt datetime'2013-01-31T09:00:00' * * @param propertyName * A String which specifies the name of the property to compare. * @param operation * A String which specifies the comparison operator to use. * @param value * A java.util.Date which specifies the value to compare with the property. * @return * A String which represents the formatted filter condition. */ public static String generateFilterCondition(String propertyName, String operation, final Date value) { return generateFilterCondition(propertyName, operation, Utility.getJavaISO8601Time(value), EdmType.DATE_TIME); } /** * Generates a property filter condition string for a double value. Creates a formatted string to use * in a filter expression that uses the specified operation to compare the property with the value, formatted as * a double value, as in the following example: *

* String condition = generateFilterCondition("Circumference", QueryComparisons.EQUAL, 2 * 3.141592); *

* This statement sets condition to the following value: *

* Circumference eq 6.283184 * * @param propertyName * A String which specifies the name of the property to compare. * @param operation * A String which specifies the comparison operator to use. * @param value * A double which specifies the value to compare with the property. * @return * A String which represents the formatted filter condition. */ public static String generateFilterCondition(String propertyName, String operation, final double value) { return generateFilterCondition(propertyName, operation, Double.toString(value), EdmType.DOUBLE); } /** * Generates a property filter condition string for an int value. Creates a formatted string to use * in a filter expression that uses the specified operation to compare the property with the value, formatted as * a numeric value, as in the following example: *

* String condition = generateFilterCondition("Population", QueryComparisons.GREATER_THAN, 1000); *

* This statement sets condition to the following value: *

* Population gt 1000 * * @param propertyName * A String which specifies the name of the property to compare. * @param operation * A String which specifies the comparison operator to use. * @param value * An int which specifies the value to compare with the property. * @return * A String which represents the formatted filter condition. */ public static String generateFilterCondition(String propertyName, String operation, final int value) { return generateFilterCondition(propertyName, operation, Integer.toString(value), EdmType.INT32); } /** * Generates a property filter condition string for a long value. Creates a formatted string to use * in a filter expression that uses the specified operation to compare the property with the value, formatted as * a numeric value, as in the following example: *

* String condition = generateFilterCondition("StellarMass", QueryComparisons.GREATER_THAN, 7000000000L); *

* This statement sets condition to the following value: *

* StellarMass gt 7000000000 * * @param propertyName * A String which specifies the name of the property to compare. * @param operation * A String which specifies the comparison operator to use. * @param value * A long which specifies the value to compare with the property. * @return * A String which represents the formatted filter condition. */ public static String generateFilterCondition(String propertyName, String operation, final long value) { return generateFilterCondition(propertyName, operation, Long.toString(value), EdmType.INT64); } /** * Generates a property filter condition string for a String value. Creates a formatted string to use * in a filter expression that uses the specified operation to compare the property with the value, formatted as * a string value, as in the following example: *

* String condition = generateFilterCondition("Platform", QueryComparisons.EQUAL, "Azure"); *

* This statement sets condition to the following value: *

* Platform eq 'Azure' * * @param propertyName * A String which specifies the name of the property to compare. * @param operation * A String which specifies the comparison operator to use. * @param value * A String which specifies the value to compare with the property. * @return * A String which represents the formatted filter condition. */ public static String generateFilterCondition(String propertyName, String operation, final String value) { return generateFilterCondition(propertyName, operation, value, EdmType.STRING); } /** * Generates a property filter condition string. Creates a formatted string to use in a filter expression that uses * the specified operation to compare the property with the value, formatted as the specified {@link EdmType}. * * @param propertyName * A String which specifies the name of the property to compare. * @param operation * A String which specifies the comparison operator to use. * @param value * A String which specifies the value to compare with the property. * @param edmType * The {@link EdmType} to format the value as. * @return * A String which represents the formatted filter condition. */ public static String generateFilterCondition(String propertyName, String operation, String value, EdmType edmType) { String valueOperand = null; if (edmType == EdmType.BOOLEAN || edmType == EdmType.DOUBLE || edmType == EdmType.INT32) { valueOperand = value; } else if (edmType == EdmType.INT64) { valueOperand = String.format("%sL", value); } else if (edmType == EdmType.DATE_TIME) { valueOperand = String.format("datetime'%s'", value); } else if (edmType == EdmType.GUID) { valueOperand = String.format("guid'%s'", value); } else if (edmType == EdmType.BINARY) { valueOperand = String.format("X'%s'", value); } else { valueOperand = String.format("'%s'", value.replace("'", "''")); } return String.format("%s %s %s", propertyName, operation, valueOperand); } /** * Generates a property filter condition string for a UUID value. Creates a formatted string to use * in a filter expression that uses the specified operation to compare the property with the value, formatted as * a UUID value, as in the following example: *

* String condition = generateFilterCondition("Identity", QueryComparisons.EQUAL, UUID.fromString( * "c9da6455-213d-42c9-9a79-3e9149a57833")); *

* This statement sets condition to the following value: *

* Identity eq guid'c9da6455-213d-42c9-9a79-3e9149a57833' * * @param propertyName * A String which specifies the name of the property to compare. * @param operation * A String which specifies the comparison operator to use. * @param value * A UUID which specifies the value to compare with the property. * @return * A String which represents the formatted filter condition. */ public static String generateFilterCondition(String propertyName, String operation, final UUID value) { return generateFilterCondition(propertyName, operation, value.toString(), EdmType.GUID); } /** * Creates a filter condition using the specified logical operator on two filter conditions. * * @param filterA * A String which specifies the first formatted filter condition. * @param operator * A String which specifies Operators.AND or Operators.OR. * @param filterB * A String which specifies the first formatted filter condition. * @return * A String which represents the combined filter expression. */ public static String combineFilters(String filterA, String operator, String filterB) { return String.format("(%s) %s (%s)", filterA, operator, filterB); } private Class clazzType = null; private String sourceTableName = null; private String[] columns = null; private Integer takeCount; private String filterString = null; /** * Initializes an empty {@link TableQuery} instance. This table query cannot be executed without * setting a table entity type. */ public TableQuery() { // empty ctor } /** * Initializes a {@link TableQuery} with the specified table entity type. Callers may specify * {@link TableServiceEntity}.class as the class type parameter if no more specialized type is * required. * * @param clazzType * The java.lang.Class of the class T that represents the table entity type for * the query. Class T must be a type that implements {@link TableEntity} and has a nullary * constructor. */ public TableQuery(final Class clazzType) { this.setClazzType(clazzType); } /** * Gets the class type of the table entities returned by the query. * * @return * The java.lang.Class of the class T that represents the table entity type for * the query. */ public Class getClazzType() { return this.clazzType; } /** * Gets an array of the table entity property names specified in the table query. All properties in the table are * returned by default if no property names are specified with a select clause in the table query. The table entity * properties to return may be specified with a call to the {@link #setColumns} or {@link #select} methods with a * array of property names as parameter. *

* Note that the system properties PartitionKey, RowKey, and Timestamp are * automatically requested from the table service whether specified in the {@link TableQuery} or not. * * @return * An array of String objects which represents the property names of the table entity properties to * return in the query. */ public String[] getColumns() { return this.columns; } /** * Gets the filter expression specified in the table query. All entities in the table are returned by * default if no filter expression is specified in the table query. A filter for the entities to return may be * specified with a call to the {@link #setFilterString} or {@link #where} methods. * * @return * A String which represents the filter expression used in the query. */ public String getFilterString() { return this.filterString; } /** * Gets the name of the source table specified in the table query. * * @return * A String which represents the name of the source table used in the query. */ protected String getSourceTableName() { return this.sourceTableName; } /** * Gets the number of entities the query returns specified in the table query. If this value is not * specified in a table query, a maximum of 1,000 entries will be returned. The number of entities to return may be * specified with a call to the {@link #setTakeCount} or {@link #take} methods. *

* If the value returned by getTakeCount is greater than 1,000, the query will throw a * {@link StorageException} when executed. * * @return * An Integer which represents the maximum number of entities for the table query to return. */ public Integer getTakeCount() { return this.takeCount; } /** * Defines the property names of the table entity properties to return when the table query is executed. The * select clause is optional on a table query, used to limit the table properties returned from the * server. By default, a query will return all properties from the table entity. *

* Note that the system properties PartitionKey, RowKey, and Timestamp are * automatically requested from the table service whether specified in the {@link TableQuery} or not. * * @param columns * An array of String objects which specify the property names of the table entity properties * to return when the query is executed. * * @return * A reference to the {@link TableQuery} instance with the table entity properties to return set. */ public TableQuery select(final String[] columns) { this.setColumns(columns); return this; } /** * Sets the class type of the table entities returned by the query. A class type is required to execute a table * query. *

* Callers may specify {@link TableServiceEntity}.class as the class type parameter if no more * specialized type is required. * * @param clazzType * The java.lang.Class of the class T that represents the table entity type for * the query. Class T must be a type that implements {@link TableEntity} and has a nullary * constructor, */ public void setClazzType(final Class clazzType) { Utility.assertNotNull("class type", clazzType); Utility.checkNullaryCtor(clazzType); this.clazzType = clazzType; } /** * Sets the property names of the table entity properties to return when the table query is executed. By default, a * query will return all properties from the table entity. *

* Note that the system properties PartitionKey, RowKey, and Timestamp are * automatically requested from the table service whether specified in the {@link TableQuery} or not. * * @param columns * An array of String objects which specify the property names of the table entity properties * to return when the query is executed. */ public void setColumns(final String[] columns) { this.columns = columns; } /** * Sets the filter expression to use in the table query. A filter expression is optional; by default a table query * will return all entities in the table. *

* Filter expressions for use with the {@link #setFilterString} method can be created using fluent syntax with the * overloaded {@link #generateFilterCondition} methods and {@link #combineFilters} method, using the comparison * operators defined in {@link QueryComparisons} and the logical operators defined in {@link Operators}. Note that * the first operand in a filter comparison must be a property name, and the second operand must evaluate to a * constant. The PartitionKey and RowKey property values are String types for comparison purposes. For * example, to query all entities with a PartitionKey value of "AccessLogs" on table query myQuery: *

*     myQuery.setFilterString("PartitionKey eq 'AccessLogs'"); *

* The values that may be used in table queries are explained in more detail in the MSDN topic * * Querying Tables and Entities, but note * that the space characters within values do not need to be URL-encoded, as this will be done when the query is * executed. *

* Note that no more than 15 discrete comparisons are permitted within a filter string. * * @param filterString * A String which represents the filter expression to use in the query. */ public void setFilterString(final String filterString) { Utility.assertNotNullOrEmpty("filterString", filterString); this.filterString = filterString; } /** * Sets the name of the source table for the table query. A table query must have a source table to be executed. * * @param sourceTableName * A String which specifies the name of the source table to use in the query. */ protected void setSourceTableName(final String sourceTableName) { Utility.assertNotNullOrEmpty("tableName", sourceTableName); this.sourceTableName = sourceTableName; } /** * Sets the upper bound for the number of entities the query returns. If this value is not specified in a table * query, by default a maximum of 1,000 entries will be returned. *

* If the value specified for the takeCount parameter is greater than 1,000, the query will throw a * {@link StorageException} when executed. * * @param takeCount * An Integer which represents the maximum number of entities for the table query to return. */ public void setTakeCount(final Integer takeCount) { if (takeCount != null && takeCount <= 0) { throw new IllegalArgumentException(SR.TAKE_COUNT_ZERO_OR_NEGATIVE); } this.takeCount = takeCount; } /** * Defines the upper bound for the number of entities the query returns. If this value is not specified in a table * query, by default a maximum of 1,000 entries will be returned. *

* If the value specified for the take parameter is greater than 1,000, the query will throw a * {@link StorageException} when executed. * * @param take * An Integer which represents the maximum number of entities for the table query to return. * * @return * A reference to the {@link TableQuery} instance with the number of entities to return set. */ public TableQuery take(final Integer take) { if (take != null) { this.setTakeCount(take); } return this; } /** * Defines a filter expression for the table query. Only entities that satisfy the specified filter expression will * be returned by the query. Setting a filter expression is optional; by default, all entities in the table are * returned if no filter expression is specified in the table query. *

* Filter expressions for use with the {@link #where} method can be created using fluent syntax with the overloaded * {@link #generateFilterCondition} methods and {@link #combineFilters} method, using the comparison operators * defined in {@link QueryComparisons} and the logical operators defined in {@link Operators}. Note that the first * operand in a filter comparison must be a property name, and the second operand must evaluate to a constant. The * PartitionKey and RowKey property values are String types for comparison purposes. For example, to * query all entities with a PartitionKey value of "AccessLogs" on table query myQuery: *

*     myQuery = myQuery.where("PartitionKey eq 'AccessLogs'"); *

* The values that may be used in table queries are explained in more detail in the MSDN topic * * Querying Tables and Entities, but note * that the space characters within values do not need to be URL-encoded, as this will be done when the query is * executed. *

* Note that no more than 15 discrete comparisons are permitted within a filter string. * * @param filter * A String which specifies the filter expression to apply to the table query. * @return * A reference to the {@link TableQuery} instance with the filter on entities to return set. */ public TableQuery where(final String filter) { this.setFilterString(filter); return this; } /** * Reserved for internal use. Creates a {@link UriQueryBuilder} object representing the table query. * * @return A {@link UriQueryBuilder} object representing the table query. * * @throws StorageException * if an error occurs in adding or encoding the query parameters. */ protected UriQueryBuilder generateQueryBuilder() throws StorageException { final UriQueryBuilder builder = new UriQueryBuilder(); if (!Utility.isNullOrEmpty(this.filterString)) { builder.add(TableConstants.FILTER, this.filterString); } if (this.takeCount != null) { builder.add(TableConstants.TOP, this.takeCount.toString()); } if (this.columns != null && this.columns.length > 0) { final StringBuilder colBuilder = new StringBuilder(); boolean foundRk = false; boolean foundPk = false; boolean roundTs = false; for (int m = 0; m < this.columns.length; m++) { if (TableConstants.ROW_KEY.equals(this.columns[m])) { foundRk = true; } else if (TableConstants.PARTITION_KEY.equals(this.columns[m])) { foundPk = true; } else if (TableConstants.TIMESTAMP.equals(this.columns[m])) { roundTs = true; } colBuilder.append(this.columns[m]); if (m < this.columns.length - 1) { colBuilder.append(","); } } if (!foundPk) { colBuilder.append(","); colBuilder.append(TableConstants.PARTITION_KEY); } if (!foundRk) { colBuilder.append(","); colBuilder.append(TableConstants.ROW_KEY); } if (!roundTs) { colBuilder.append(","); colBuilder.append(TableConstants.TIMESTAMP); } builder.add(TableConstants.SELECT, colBuilder.toString()); } return builder; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy