zoukankan      html  css  js  c++  java
  • ElasticSearch常用结构化搜索

    最近,需要用到ES的一些常用的结构化搜索命令,因此,看了一些官方的文档,学习了一下。结构化查询指的是查询那些具有内在结构的数据,比如日期、时间、数字都是结构化的。

    它们都有精确的格式,我们可以对这些数据进行逻辑操作,比较常见的操作包括比较时间区间,或者获取两个数字间的较大值。

    精确查询

    当进行精确查询时,过滤器filter是十分重要的,因为它们效率非常高,过滤器不计算相关性(直接跳过了整个记分阶段)而且很容易进行缓存。

    过滤数字

    我们首先看 term filter,它最常用,可以用来处理数字,布尔值,日期和文本。

    例如我们有一些产品:

    POST /my_store/products/_bulk
    { "index": { "_id": 1 }}
    { "price" : 10, "productID" : "XHDK-A-1293-#fJ3" }
    { "index": { "_id": 2 }}
    { "price" : 20, "productID" : "KDKE-B-9947-#kL5" }
    { "index": { "_id": 3 }}
    { "price" : 30, "productID" : "JODL-X-1937-#pV7" }
    { "index": { "_id": 4 }}
    { "price" : 30, "productID" : "QQPX-R-3956-#aD8" }

    我们想要做的是要查询具有某个价格的所有产品,如果对于SQL熟悉,那么它的表达式是:

    SELECT * FROM   products WHERE  price = 20

    在ES查询中,我们使用 term 达到相同的目的:

    {
        "term" : {
            "price" : 20
        }
    }

    但是在ES里,term 不能单独使用,search API期望的是一个 query 而不是 filter,所以,我们需要把 term 放在一个filter query里进行使用:

    GET /my_store/products/_search
    {
        "query" : {
            "filtered" : { #filtered 查询同时接受一个 query 和 filter
                "query" : {
                    "match_all" : {} #match_all 会返回所有匹配的文件,这是个默认行为
                },
                "filter" : {
                    "term" : { #term 过滤我们之前说到的,需要注意的是这里 term块 是处于 filter 之内的
                        "price" : 20
                    }
                }
            }
        }
    }

    执行结果正如我们期望一样,它只会返回文档2,这里我们称为命中hit。

    "hits" : [
        {
            "_index" : "my_store",
            "_type" :  "products",
            "_id" :    "2",
            "_score" : 1.0, #1
            "_source" : {
              "price" :     20,
              "productID" : "KDKE-B-9947-#kL5"
            }
        }
    ]

     之前我们说到filter不会进行记分或相关性计算,这里的分数来自于我们查询时使用的关键字 match_all ,它会同等对待所有的文件,并对所有的结果都给以1的记分。

    过滤文本

    term 同样可以用来过滤文本,如果我们想要查询某个具体UPC id的产品,SQL语句会是下面这样:

    SELECT product FROM   products WHERE  productID = "XHDK-A-1293-#fJ3"

    转换成ES查询,同样使用 term 来查询:

    GET /my_store/products/_search
    {
        "query" : {
            "filtered" : {
                "filter" : {
                    "term" : {
                        "productID" : "XHDK-A-1293-#fJ3"
                    }
                }
            }
        }
    }

    但这里有个小问题,我们没有如预期得到想要的结果!为什么呢?问题并不出在 term 查询上,问题出在数据索引的方式。如果使用 analyze API(Test Analyzers),我们可以看到这里的UPC码以及被拆分成多个小的token:

    GET /my_store/_analyze?field=productID
    XHDK-A-1293-#fJ3

    结果

    {
      "tokens" : [ {
        "token" :        "xhdk",
        "start_offset" : 0,
        "end_offset" :   4,
        "type" :         "<ALPHANUM>",
        "position" :     1
      }, {
        "token" :        "a",
        "start_offset" : 5,
        "end_offset" :   6,
        "type" :         "<ALPHANUM>",
        "position" :     2
      }, {
        "token" :        "1293",
        "start_offset" : 7,
        "end_offset" :   11,
        "type" :         "<NUM>",
        "position" :     3
      }, {
        "token" :        "fj3",
        "start_offset" : 13,
        "end_offset" :   16,
        "type" :         "<ALPHANUM>",
        "position" :     4
      } ]
    }

    所以,当我们用 term 去过滤值 XHDK-A-1293-#fJ3 的时候,找不到任何文件,因为这个token不在我们的反向索引(inverted index)之中,正如上面呈现的,索引里面有4个token。

    显然,这种对于id码或其他任何精确值的处理方式不是我们想要的。

    为了避免这种问题,我们需要告诉ElasticSearch这个字段具有精确值,需要被设置成 not_analyzed 。 我们可以在定制化字段mapping中找到相关内容。为了修正这个问题,我们需要首先删除老的index,然后再创建一个新的

    DELETE /my_store #1
    
    PUT /my_store #2
    {
        "mappings" : {
            "products" : {
                "properties" : {
                    "productID" : {
                        "type" : "string",
                        "index" : "not_analyzed" #3
                    }
                }
            }
        }
    
    }

    然后我们就可以对文件重索引了:

    POST /my_store/products/_bulk
    { "index": { "_id": 1 }}
    { "price" : 10, "productID" : "XHDK-A-1293-#fJ3" }
    { "index": { "_id": 2 }}
    { "price" : 20, "productID" : "KDKE-B-9947-#kL5" }
    { "index": { "_id": 3 }}
    { "price" : 30, "productID" : "JODL-X-1937-#pV7" }
    { "index": { "_id": 4 }}
    { "price" : 30, "productID" : "QQPX-R-3956-#aD8" }

    组合过滤器

    上面的两个例子都是单个filter的使用方式,在实际中,我们很多情况下会同时会对多个值或字段使用filter。例如,在ElasticSearch中,如何标识下面这个SQL?

    SELECT product FROM   products WHERE  (price = 20 OR productID = "XHDK-A-1293-#fJ3") AND  (price != 30)

    在这种情况下,我们需要 bool filter。这是一个复合过滤器可以接收多个参数,然后将他们组合成布尔组合。

    布尔过滤器(Bool Filter)

    bool filter包括三部分:

    {
       "bool" : {
          "must" :     [],
          "should" :   [],
          "must_not" : [],
       }
    }
    • must:所有的语句必须匹配,与 AND 等价。

    • must_not:所有的语句都不能匹配,与 NOT 等价。

    • should:至少有一个语句匹配,与 OR 等价。

    用ES查询实现我们上面SQL里的查询:

    GET /my_store/products/_search
    {
       "query" : {
          "filtered" : { 
             "filter" : {
                "bool" : {
                  "should" : [
                     { "term" : {"price" : 20}},
                     { "term" : {"productID" : "XHDK-A-1293-#fJ3"}} 
                  ],
                  "must_not" : {
                     "term" : {"price" : 30}
                  }
               }
             }
          }
       }
    }

    我们搜索的结果返回了2个hits,两个文件各满足其中一个条件:

    "hits" : [
        {
            "_id" :     "1",
            "_score" :  1.0,
            "_source" : {
              "price" :     10,
              "productID" : "XHDK-A-1293-#fJ3" 
            }
        },
        {
            "_id" :     "2",
            "_score" :  1.0,
            "_source" : {
              "price" :     20, 
              "productID" : "KDKE-B-9947-#kL5"
            }
        }
    ]

    嵌套布尔过滤器(Nesting Boolean Filters)

    尽管 bool 是一个复合的过滤器,可以接受多个子过滤器,需要注意的是 bool 过滤器本身仍然是一个过滤器(filter)。这意味着我们可以将一个bool过滤器置于另外一个bool过滤器内部,这为我们提供了复杂布尔逻辑的处理能力:

    对于一个SQL语句:

    SELECT document FROM   products WHERE  productID  = "KDKE-B-9947-#kL5" OR ( productID = "JODL-X-1937-#pV7" AND price = 30 )

    我们将其转换成一个嵌套的 bool 过滤器:

    GET /my_store/products/_search
    {
       "query" : {
          "filtered" : {
             "filter" : {
                "bool" : {
                  "should" : [
                    { "term" : {"productID" : "KDKE-B-9947-#kL5"}}, #1
                    { "bool" : { #2
                      "must" : [
                        { "term" : {"productID" : "JODL-X-1937-#pV7"}}, #3
                        { "term" : {"price" : 30}} #4
                      ]
                    }}
                  ]
               }
             }
          }
       }
    }

    得到的结果有两个文件,他们各满足 should 中的一个条件:

    "hits" : [
        {
            "_id" :     "2",
            "_score" :  1.0,
            "_source" : {
              "price" :     20,
              "productID" : "KDKE-B-9947-#kL5" #1
            }
        },
        {
            "_id" :     "3",
            "_score" :  1.0,
            "_source" : {
              "price" :      30, #2
              "productID" : "JODL-X-1937-#pV7" #3
            }
        }
    ]
  • 相关阅读:
    写一写这几天安卓开发遇到的坑
    安卓环境搭建
    正则表达式
    一段时间的总结
    路飞-登录页面
    路飞-腾讯云短信接口
    路飞-Redis
    路飞-git操作
    路飞-后台xadmin配置
    路飞-后台处理跨域问题
  • 原文地址:https://www.cnblogs.com/junjiang3/p/7537702.html
Copyright © 2011-2022 走看看