com.microsoft.azure.storage.table.TableQuery Maven / Gradle / Ivy
Show all versions of azure-storage Show documentation
/**
* 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;
}
}