50

How can I set multiple filters on a Azure Table Storage?

This is what I've tried:

string partitionFilter = TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, "partition1");
string date1 = TableQuery.GenerateFilterCondition("Date", QueryComparisons.GreaterThanOrEqual, "31-8-2013T14:15:14Z");
string date2 = TableQuery.GenerateFilterCondition("Date", QueryComparisons.LessThanOrEqual, "31-8-2013T14:15:14Z");
string finalFilter = TableQuery.CombineFilters(partitionFilter, TableOperators.And, date1);

This doesn't work because TableQuery.CombineFilters() only takes 3 parameters. And I need an extra parameter for the 2nd date.

My second try:

string filter = "PartitionKey eq 'partition1' and Date ge datetime'31-8-2013T14:15:14Z' and Date lt datetime'31-8-2013T14:19:10Z'";
TableQuery<CustomEntity> query = new TableQuery<CustomEntity>().Where(filter).Take(5);

This returns 400 bad request. But if I remove the 'datetime' it runs but returns no results while it should return a few 100 records.

According to this doc from msdn, that is how datetimes should be formatted.

My result should be all records that are between two dates.

How can I make this work?

Quoter
  • 4,236
  • 13
  • 47
  • 69

5 Answers5

72

First "and" your partition filter with one of the date filters, then "and" the intermediate result with the other date filter.

string date1 = TableQuery.GenerateFilterConditionForDate(
                   "Date", QueryComparisons.GreaterThanOrEqual,
                   DateTimeOffsetVal);
string date2 = TableQuery.GenerateFilterConditionForDate(
                   "Date", QueryComparisons.LessThanOrEqual,
                   DateTimeOffsetVal);
string finalFilter = TableQuery.CombineFilters(
                        TableQuery.CombineFilters(
                            partitionFilter,
                            TableOperators.And,
                            date1),
                        TableOperators.And, date2);
Pavel Chuchuva
  • 22,633
  • 10
  • 99
  • 115
Damith
  • 62,401
  • 13
  • 102
  • 153
  • 2
    I tried your solution, it compiles and runs, but no records are returned. Are there any special 'things' i have to do for `DateTime` comparisions that you are aware of? – Quoter Aug 31 '13 at 16:22
  • 3
    It seems that there are different enums for every datatype of filter you do. And for date it's `TableQuery.GenerateFilterConditionForDate`. And this did the trick. Damith, can you set this too in your answer? – Quoter Aug 31 '13 at 18:07
17

I am using Windows Azure Storage 7.0.0 and you can use Linq query so that you don't need to combine filters anymore:

// filter dates for test
var startDate = DateTime.Parse("01/02/2016 12:00:00 AM"); 
var endDate = DateTime.Parse("02/02/2016 12:00:00 AM");

// Get the cloud table
var cloudTable = GetCloudTable();

// Create a query: in this example I use the DynamicTableEntity class
var query = cloudTable.CreateQuery<DynamicTableEntity>()
        .Where(d => d.PartitionKey == "partition1"
               && d.Timestamp >= startDate && d.Timestamp <= endDate);

// Execute the query
var result = query.ToList();

Here is the generated query :

((PartitionKey eq 'partition1') and (Timestamp ge datetime'2016-01-31T11:00:00Z')) and (Timestamp le datetime'2016-02-01T11:00:00Z')

You can notice that:

  • The filters have been combined.
  • The dates have been converted to UTC.
Thomas
  • 24,234
  • 6
  • 81
  • 125
  • Does it a hard requirement for the partition and row keys (if used) are provided in the query? Otherwise, how do I ensure I'm not scanning the entire table? – CamHart Mar 22 '18 at 19:05
  • @CamHart If you don't specify PartitionKey and RowKey, you are going to perform a table scan – Deepak Sep 11 '20 at 11:11
15

How can I set multiple filters on a Azure Table Storage?

I was wondering the same thing. I wrote an extension to the TableQuery class which works fine.

It's an easy change which makes me wonder if we are going about querying with multiple filters incorrectly.

public static class TableQueryExtensions 
{
    public static TableQuery<TElement> AndWhere<TElement>(this TableQuery<TElement> @this, string filter)
    {
        @this.FilterString = TableQuery.CombineFilters(@this.FilterString, TableOperators.And, filter);
        return @this;
    }

    public static TableQuery<TElement> OrWhere<TElement>(this TableQuery<TElement> @this, string filter)
    {
        @this.FilterString = TableQuery.CombineFilters(@this.FilterString, TableOperators.Or, filter);
        return @this;
    }

    public static TableQuery<TElement> NotWhere<TElement>(this TableQuery<TElement> @this, string filter)
    {
        @this.FilterString = TableQuery.CombineFilters(@this.FilterString, TableOperators.Not, filter);
        return @this;
    }
}
LivingOnACloud
  • 1,151
  • 1
  • 11
  • 20
  • If you are adding multiple `Or` filters, then `FilterString` will look like `((() or (Foo eq '1')) or (Foo eq '2')) or (Foo eq '3')`. If `FilterString` is empty, add just the filter itself: `@this.FilterString = @this.FilterString == null ? filter : TableQuery.CombineFilters(@this.FilterString, TableOperators.Or, filter);`. – Kristoffer Jälén Sep 13 '16 at 14:49
10

Just wanted to add one more answer.

string filter = "PartitionKey eq 'partition1' and Date ge datetime'31-8-2013T14:15:14Z' and Date lt datetime'31-8-2013T14:19:10Z'";
TableQuery<TablePost> query = new TableQuery<TablePost>().Where(filter).Take(5);

The reason code above is failing is because the date/time value must be entered in yyyy-MM-ddTHH:mm:ssZ format. So your query should be:

string filter = "(PartitionKey eq 'partition1') and (Date ge datetime'2013-08-31T14:15:14Z' and Date lt datetime'2013-08-31T14:19:10Z')";
TableQuery<TablePost> query = new TableQuery<TablePost>().Where(filter).Take(5);
Gaurav Mantri
  • 128,066
  • 12
  • 206
  • 241
3

Just handling the case of a new query that does not have a filter already and based on @LivingOnACloud, I would rather write it this way:

 public static TableQuery<TElement> AndWhere<TElement>(this TableQuery<TElement> query, string filter)
            where TElement : ITableEntity,new ()
        {
            if (query.FilterString.IsNullOrEmpty())
            {
                query.FilterString =  filter;
            }
            else
            {
                query.FilterString = TableQuery.CombineFilters(query.FilterString, TableOperators.And, filter);
            }
            return query;
        }

And the rest follow the same check, things can go nicer.

Assil
  • 572
  • 6
  • 21