zoukankan      html  css  js  c++  java
  • Func<T,T>应用之Elasticsearch查询语句构造器的开发

    前言

      之前项目中做Elasticsearch相关开发的时候,虽然借助了第三方的组件PlainElastic.Net,但是由于当时不熟悉用法,而选择了自己拼接查询语句。例如:

                string queryGroup = "{"query": {"match": { "roomid": "FRIEND_12686_10035" }}}";
                //关键字查询
                string queryKeyWord = "{ "query": {"match_phrase": {"content": {"query": "" + keyword + "","slop": 0} } }}";
                //是否图片 查询
                string queryImg = "{ "term": {"isimg": true }}";
                //是否包含文件查询
                string queryFile = "{ "term": {"isfile": true }}";
                //大于小于某个时间段查询
                string queryTimeRange = "{"range": {"addtime": { "gt": "" + st + "","lt": "" + et + "" }} }";
                //大于某个时间
                string queryTimeRangeGt = "{"range": {"addtime": { "gt": "" + st + ""}} }";
                //小于某个时间
                string queryTimeRangeLt = "{"range": {"addtime": { "lt": "" + et + "" }} }";

      后来慢慢看了下该组件的源代码,想自己简单实现一下,看看到底是什么原理。

    分析

      先来一个简单的小例子:PlainElastic中的demo示例:

          string query = new QueryBuilder<Tweet>()        // This will generate: 
              .Query(q => q                         // { "query": { "term": { "User": "somebody" } } }
                .Term(t => t
                  .Field(tweet=> tweet.User).Value("somebody")
                )
              ).Build();

      可以看到,构造查询语句的时候很灵活,直接用表达式的形式,最后通过Build方法,生成相应的查询语句,于是乎,照着葫芦画瓢,开始吧。其实,不管如何写语句,最终都是对字符串的拼接,生成最终的查询语句。那我们就从最简单的term查询开始。比如一条查询语句就是 {"query":{"term":{"name":"zhangsan"}}},这条语句的的意思,就是查询 name 为zhangsan的数据。(需要读者懂elasticsearch查询语法)

      先不考虑封装,直接新建一个类,就叫 TermFilter,内部实现了 Term 的语言构造。由于需要链式调用,所以里面的方法一般都返回  this 。

            private Dictionary<string, object> _terms;
    public TermFilter() { _terms = new Dictionary<string, object>(); } public TermFilter KeyValue(string key, object value) { _terms.Add(key, value); return this; }

      如上述代码所示,当我们调用KeyValue方法时,传入key和value,添加到内部的Dictionary中。然后重写 ToString 方法,构造Term语句

           private void Build()
            {
                StringBuilder str = new StringBuilder();
                int i = 0;
                foreach (KeyValuePair<string, object> kv in _terms)
                {
                    str.Append("{"term":{"" + kv.Key + "":" + kv.Value + "}}");
                    if (i >= 0 && i < _terms.Count - 1)
                    {
                        str.Append(",");
                    }
                    i++;
                }
                _condition = str.ToString();
            }
            public override string ToString()
            {
                Build();
    
                return base.ToString();
            }

      遍历Dictionary,构造term语句,Term构造完之后,我们需要在外层加一个Query,由于Query是通用的,所以也需要提取出。于是乎,又多了一个类,叫做Filter,这个是查询的入口,里面有两个方法,一个Bool方法,一个Query方法:

         public Filter() { }
            //
            public Filter Bool(Func<BoolFilter, BoolFilter> boolFunc)
            {
                string boolFuncResult = boolFunc(new BoolFilter()).ToString();
                _condition = "{"query":{"filtered":{"filter":{" + boolFuncResult + "}}}";
                return this;
            }
            public Filter Query(Func<BoolFilter, BoolFilter> boolFunc)
            {
                string boolFuncResult = boolFunc(new BoolFilter()).ToString();
                _condition = "{"query":" + boolFuncResult;
                return this;
            }

      直接看Query方法,里面的参数为 Func<BoolFilter,BoolFilter> boolFunc,好吧,这里的Boss终于出场了,就是核心类,BoolFilter,它内部实现了,Must,Shoud,MustNot,And,Or,等方法。当然还有Term。我们直接看Term方法。

     public BoolFilter Term(Func<TermFilter, TermFilter> termFunc)
            {
                PrapareCondition();
                _condition += termFunc(new TermFilter());
                return this;
            }

      同理,因为链式调用,还是返回this,上述代码中由于termFunc 返回的是一个TermFilter对象,然后toString之后,就相当于追加 相应的Term语句。ToString方法最终也是返回 _condition字段的值。好吧,我猜你越来越晕了,没关系,我们在看最后一个类,就可以实战了。

          public QueryCreator Filter(Func<Filter, Filter> filter)
            {
                _condition += filter(new Filter());
                return this;
            }

      好了,到这里,代码基本结束了。重新梳理一遍:

      首先,最外层代码调用Filter方法,Filter实现了Query方法,Query内部传入了BoolFilter参数,在调用Term方法,最终由TermFilter实现语句的构造,所以,外部最终代码调用起来是这样的。

    var result = creator.Filter(f =>  //Filter内部调用Query方法,
                                                f.Query(q => //Query调用BoolFilter的Term方法
                                                            q.Term(t => //BoolFilter又调用TermFilter的KeyValue方法
                                                                        t.KeyValue("name", "zhangsan"))))
                                                                        .BuildBeautiful();//最后构造出我们想要的结果

      

    如上图,from和size是默认的。下面我们来个稍微复杂点的。比如在一个用户表,想要查询 用户类型为 3 的且地区为 北京 的 并且满足 年龄是 20  岁或者 工作经验为1年 的用户。并且根据 姓名倒叙排序,分页取第3页的20条数据。

    首先分析一下,这里我们要使用 and 查询,and 里面还包括  or 查询。 构造语句如下:

            var result = creator.Filter(f => 
                                                f.Bool(b => //bool查询
                                                b.Must(m => //must,必须符合条件
                                                m.And(a => //and查询
                                                a.Term(t => //构造查询条件
                                                t.KeyValue("type", 3).KeyValue("area", "北京")).
                                                Or(o => o.Term(t1 => t1.KeyValue("age", 20).KeyValue("experience", 1))))))).//or查询,构造查询条件
                                                Page(3).//页码
                                                Size(20).//每页大小
                                                OrderByDesc("name").//姓名倒叙排序
                                                BuildBeautiful();//根据之前的条件创建查询语句

      哈哈,是不是有点绕啊,其实要想用这个,还是得会点ES查询语法的,就好比SQL语句一样,你不理解,是查不出东东的。用这种方式我们就能够避免手动写ES查询语句了,只要经过代码简单配制就好喽,不过我还是乖乖用第三方组件吧,自己写的太渣了。。。

    看看构造成的语句:

    好了就到这里吧。当做自己研究的总结了。

  • 相关阅读:
    【年度总结】——踏雪留痕
    ios提交程序后出现的各种问题
    c++动态库中使用命名空间的问题
    第八章 网络的时代—网络开发(4)
    USB otg 学习笔记
    servlet_3
    Windows server 2012清除并重建SID
    实时监听输入框值变化:oninput & onpropertychange
    JQuery 自动触发事件
    jquery input change事件
  • 原文地址:https://www.cnblogs.com/panzi/p/6208635.html
Copyright © 2011-2022 走看看