设为首页 收藏本站
查看: 1129|回复: 0

[经验分享] Azure 基础:自定义 Table storage 查询条件

[复制链接]

尚未签到

发表于 2017-6-29 23:45:16 | 显示全部楼层 |阅读模式
  本文是在 《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 是本文的主角,它代表了某个表上的一个查询。基本用法是使用查询条件构建一个 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 的实现来说明如何利用现有的类型及方法来实现自定义查询的过滤条件。对于有类似需求的朋友,希望能起到抛砖引玉的作用。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-389491-1-1.html 上篇帖子: 初码-Azure系列-记一次从阿里云到Azure的迁移和部署 下篇帖子: Azure SQL Database Active Geo-Replication简介
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表