天天看點

自定義 Azure Table storage 查詢過濾條件

本文通過實作 StartsWith 的過濾條件,來說明如何自定義 Azure Table storage 的查詢過濾條件。

本文是在Azure Table storage 基本用法一文的基礎上,介紹如何自定義 Azure Table storage 的查詢過濾條件。如果您還不太清楚 Azure Table storage 的基本用法,請先移步前文。

讓我們回到前文中提到的一個問題,如何過濾出 MyLogTable 表中某一天産生的所有日志?在進入細節之前,我們先來回顧一下 MyLogTable 類的設計:

internal class MyLogEntity : TableEntity
{
    public MyLogEntity() { }
    public MyLogEntity(string pkey, string rkey)
    {
        this.PartitionKey = pkey;
        this.RowKey = rkey;
    }
    //…
}       

其中,PartitionKey 用來存放産生日志的年份和月份(例如201607表示2016年7月),RowKey 用來存放産生日志的天和時分秒毫秒(例如160934248492表示16号9點34分…)。

在我們設計的 MyLogTable 中,天資訊儲存在 RowKey 的前兩位。我們要做的就是過濾 RowKey 的前兩位,也就是找到所有 RowKey 以”xx”開頭的記錄。這在字元串操作中稱為 StartsWith。遺憾的是現有 Table storage 的接口中沒有提供這種功能的方法,是以我們需要我們自己實作它(還好 TableQuery 的實作支援我們去擴充它)。

本文将通過實作 StartsWith 過濾條件說明如何自定義 Azure Table storage 的查詢過濾條件。

TableQuery類

TableQuery 是本文的主角,它代表了 Table 上的一個查詢。基本用法是使用查詢條件建構一個 TableQuery 類的執行個體,然後把這個執行個體作為參數傳遞給 CloudTable 的 ExecuteQuery 方法:

TableQuery<MyLogEntity> query = new TableQuery<MyLogEntity>().Where(
    TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, "201607"));
var queryResult = logTable.ExecuteQuery(query);      

我們還可以使用 TableQuery 的靜态方法 CombineFilters 建構自定義的查詢條件。

比如我們要查詢 PartitionKey 等于 "201607" 并且 RowKey 等于"161148372454"的記錄:

TableQuery.CombineFilters(
    TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, "201607"),
    TableOperators.And,
    TableQuery.GenerateFilterCondition("RowKey", QueryComparisons.Equal, "161148372454"));      

此時函數的傳回結果為: ” (PartitionKey eq '201607') and (RowKey eq '161148372454')”。

然後把這個過濾字元串送給 query.Where 函數做參數,或者設定給 query.FilterString 屬性,就可以完成過濾功能了。

CombineFilters 方法可愛的地方在于我們可以不斷的使用它來合并查詢條件,直到滿意為止!

接下來我們一起看看 StartsWith 過濾條件的實作過程。

比較字元串

如何從一些字元串中找出以某個子串開頭的字元串呢?我們可以從字元串的比較入手。

比如字元串具有下面的關系:

“abc”  ==  “abc” < “abd”

“abc” < “abca” < “abd”

“abc” < “abcz” < “abd”

由上面的大小關系我們可以得出結論:以”abc”開頭的字元串必定大于或等于”abc”且小于”abd”。OK,這就是我們建構 StartsWith 過濾條件的理論基礎。

建構StartsWith過濾條件

接下來我們通過 TableQuery.CombineFilters 方法建構 StartsWith 過濾條件:

string startsWithCondition = TableQuery.CombineFilters(
    TableQuery.GenerateFilterCondition("RowKey", QueryComparisons.GreaterThanOrEqual, "abc"),
    TableOperators.And,
    TableQuery.GenerateFilterCondition("RowKey", QueryComparisons.LessThan, "abd")
    );      

TableQuery.CombineFilters 方法的傳回值是一個字元串。運作上面的代碼我們會得到字元串:

“(RowKey ge 'abc') and (RowKey lt 'abd')”

我們完全可以手動拼出這樣的字元串,但我相信沒有程式員願意這麼做。

那麼,我們需要繼續完善上面的方法:

string startStr = "abc";
int endIndex = startStr.Length - 1;
Char lastChar = startStr[endIndex];
//找到比字元'c'大的那個字元。
Char afterLastChar = (char)(lastChar + 1);
//拼出字元串 "abd"
string endStr = startStr.Substring(0, endIndex) + afterLastChar;
string startsWithCondition = TableQuery.CombineFilters(
    TableQuery.GenerateFilterCondition("RowKey", QueryComparisons.GreaterThanOrEqual, startStr),
    TableOperators.And,
    TableQuery.GenerateFilterCondition("RowKey", QueryComparisons.LessThan, endStr)
    );      

組合更多過濾條件

在前面建構 StartsWith 過濾條件時,我們已經使用 TableQuery.CombineFilters 方法組合了不同的過濾條件。遺憾的是 TableQuery.CombineFilters 方法隻有兩個參數的重載,我們不能添加更多的 TableOperators 操作。

但我們可以繼續調用 TableQuery.CombineFilters 方法來組合上一個結果和新的條件。比如我們要把 Startswith 過濾條件和 PartitionKey 過濾條件組合起來就可以這麼做:

string filterCondition = TableQuery.CombineFilters(
    TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, "201607"),
    TableOperators.And,
    "(RowKey ge 'abc') and (RowKey lt 'abd')"
    );      

運作上面的代碼,生成的結果為:

(PartitionKey eq '201607') and ((RowKey ge 'abc') and (RowKey lt 'abd'))

到這來就很清楚了,TableQuery.CombineFilters 方法的主要工作,就是把過濾條件組織成查詢引擎能夠識别的字元串。

因而我們可以通過不斷的疊加,來生成很複雜的過濾條件。

封裝StartsWith過濾條件

下面我們把 StartsWith 的邏輯封裝到 StartsWithByRowKey 類型中,以下是完整的代碼:

public class MyLogEntity : TableEntity
{
     public MyLogEntity() { }
     public MyLogEntity(string pkey, string rkey)
     {
         this.PartitionKey = pkey;
         this.RowKey = rkey;
     }

     public DateTime LogDate { get; set; }
     public string LogMessage { get; set; }
     public string ErrorType { get; set; }
}

public class StartsWithByRowKey : IQuery<CloudTable, List<MyLogEntity>>
{
     private readonly string partitionKey;
     private readonly string startsWithString;

     internal StartsWithByRowKey(string partitionKey,
         string startsWithString)
     {
         this.partitionKey = partitionKey;
         this.startsWithString = startsWithString;
     }

     public List<MyLogEntity> Execute(CloudTable coludTable)
     {
         var query = new TableQuery<MyLogEntity>();

         int endIndex = startsWithString.Length - 1;
         Char lastChar = startsWithString[endIndex];
         Char afterLastChar = (char)(lastChar + 1);
         string endStr = startsWithString.Substring(0, endIndex) + afterLastChar;

         string startsWithCondition = TableQuery.CombineFilters(
             TableQuery.GenerateFilterCondition("RowKey", QueryComparisons.GreaterThanOrEqual, startsWithString),
             TableOperators.And,
             TableQuery.GenerateFilterCondition("RowKey", QueryComparisons.LessThan, endStr)
             );

         string filterCondition = TableQuery.CombineFilters(
             TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, partitionKey),
             TableOperators.And,
             startsWithCondition
             );

         var entities = coludTable.ExecuteQuery(query.Where(filterCondition));
         return entities.ToList();
     }
 }

 public interface IQuery<in TModel, out TResult>
 {
     TResult Execute(TModel model);
 }      

應用StartsWith的執行個體

現在查詢 PartitionKey 為”201607”,RowKey 以”16”開頭的記錄可以這麼寫:

StartsWithByRowKey myStartsWithQuery = new StartsWithByRowKey("201607", "16");
List<MyLogEntity> result = myStartsWithQuery.Execute(logTable);      

代碼簡潔了很多,讀起來也更清晰了(您還可以動手給 PartitionKey 添加同樣的功能)!

小結一下,本文簡單的介紹了 TableQuery 類型,然後比較詳細的說明了 StartsWith 過濾條件的思路及實作。主要是想通過 StartsWith 的實作來說明如何利用現有的類型和方法來實作自定義查詢的過濾條件。對于有類似需求的朋友,希望能起到抛磚引玉的作用。

相關閱讀:

最全的Windows Azure學習教程彙總

Azure Blob Storage 基本用法 -- Azure Storage 之 Blob

Azure Queue Storage 基本用法 -- Azure Storage 之 Queue

Azure File Storage 基本用法 -- Azure Storage 之 File

Azure Table storage 基本用法 -- Azure Storage 之 Table

本文是由葡萄城技術開發團隊釋出,轉載請注明出處:葡萄城官網

繼續閱讀