es深入搜索之结构化搜索

  结构化搜索(Structured search) 是指有关探询那些具有内在结构数据的过程。比如日期、时间和数字都是结构化的:它们有精确的格式,我们可以对这些格式进行逻辑操作。比较常见的操作包括比较数字或时间的范围,或判定两个值的大小。

1.精确值查找

  当使用精确值查找时,我们会使用过滤器,由于它不会计算相关度,所以它的查询速度相对较快。

  我们首先来看最为常用的 term 查询, 可以用它处理数字、布尔值、日期以及文本。使用方式如下:

GET /index/type/_search
{
    "query" : {
        "constant_score" : {    //表示该次查询不计算相关度,常使用它将查询转化为过滤
             "filter" : {
                "term" : {
                     "filed" : value
                }
            }
        }
    }
}
2.bool组合过滤器

  在第一部分中,我们讲了单个过滤器的使用,在日常生活中,我们经常要使用多个过滤器来进行筛选结果,比如我们要选择华为品牌,64G, 价格在2k-4k之间的手机,我们要使用组合过滤器对结果进行筛选。

  一个过滤器包括三种取值:
(1)must: 表示必须匹配,相当于AND。
(2)must_not: 表示必须不匹配,相当于NOT。
(3)should:表示任一匹配即可,相当于OR。
  使用案例如下(和传统的sql语句相同,bool过滤器也支持嵌套):

查询出产品为三星NOTE7,或者 产品为华为而且价格为30的记录
GET /index/type/_search
{
   "query" : {
      "filtered" : {
         "filter" : {
            "bool" : {
              "should" : [
                { "term" : {"productID" : "三星NOTE7"}},
                { "bool" : {
                  "must" : [
                    { "term" : {"productID" : "华为"}},
                    { "term" : {"price" : 30}}
                    ]
                }}
              ],
              "minimum_should_match": 1
           }
         }
      }
   }
}

  注意:
  1 . bool 查询会为每个文档计算相关度评分 _score ,再将所有匹配的 must 和 should 语句的分数 _score 求和,最后除以 must 和 should 语句的总数。其中must_not 语句不会影响评分;它的作用只是将不相关的文档排除。
  2 .所有 must 语句必须匹配,所有 must_not 语句都必须不匹配,默认情况下,没有 should 语句是必须匹配的,只有一个例外:那就是当没有 must 语句的时候,至少有一个 should 语句必须匹配。可以通过设置minimum_should_match参数来设置必须匹配的should语句,他可以使数字也可以是百分比。

3.查询多个精确值

  term查询在搜索单个值十分有用,但是我们如何搜索满足多个条件的值呢?比如要搜索价格为20的手机,相当于传统sql语句中的in ,当然使用bool查询是一种方式,es给我们提供了terms查询,使用方式如下:

{
    "terms" : {
        "price" : [20, 30]
    }
}

   注意:term和terms的操作是包含而非精确相等,比如我们使用该语句{ "term" : { "tags" : "search" } }进行过滤时,他会和以下两个文档同时匹配:

{ "tags" : ["search"] }
{ "tags" : ["search", "open_source"] }

  如果向要精确匹配,最好的操作就是增加一个字段,用以存储该字段包含项的数量,用以上为例

{ "tags" : ["search"], "tag_count" : 1 }
{ "tags" : ["search", "open_source"], "tag_count" : 2 }

在搜索条件时,将语句修改为bool查询的must操作,让tags的值为search,且tag_count的值为1

"bool" : {
        "must" : [
             { "term" : { "tags" : "search" } }, 
             { "term" : { "tag_count" : 1 } }
         ]
 }
4.范围

   到目前为止,我们都介绍了如何处理相等查询,对于数字过滤查询有时会更有用,比如在淘宝上搜索手机,可以在首页的头部可以选择价格区间,用来更快的找到满足自己价位的手机。

"range" : {
    "price" : {
        "gte" : greaterValue,
        "lte" : lessValue
    }
}
gt: > 大于(greater than)
lt: < 小于(less than)
gte: >= 大于或等于(greater than or equal to)
lte: <= 小于或等于(less than or equal to)

range查询不只是能用在数字上,其他类型也可以使用,比如日期,字符串

  1. 日期查询
eg: 查询过去一小时内的所有文档
"range" : {
    "timestamp" : {
        "gt" : "now-1h"
    }
}

  日期计算还可以应用到具体的时间,并非只能是now这样的占位符,只需要增加一个双管符号||并紧跟一个一个日期表达式。更多详细内容请看时间格式参考文档。

eg: 小于2020-01-07加一个月也就是 2020-02-07之前,2020-01-07之后的所有文档
"range" : {
    "timestamp" : {
        "gt" : "2020-01-07 00:00:00",
        "lt" : "2020-01-07 00:00:00||+1M"
    }
}

2.字符串范围
es在给字符串范围操作时,是使用的字典序或这字母顺序进行过滤的。

eg:查询文档字段title中只包含a的文档。
"range" : {
    "title" : {
        "gte" : "a",
        "lt" :  "b"
    }
}

注意:数字和日期范围的过滤会提高搜索性能,但是字符串有可能并非如此,在对字符串过滤时,es会对每个词项都进行term过滤。

5.关于缓存

es对非评分的行为,会对结果进行缓存。
  1. 查找匹配文档

  2. 创建bitset对结果进行缓存,该bit有一个只包含0和1的数组,用来标记那个文档匹配。

比如我们有三个doc
 doc1: { price : 20, productid : 1 }
 doc2: { price : 30, productid : 2 }
 doc3: { price : 40, productid : 3 }
 进行过滤查询 : term: { productid : 3 }
此时会创建bitset数组 [ 0, 0, 1]

  3. 如果有多个过滤查询,会迭代bitsets,对结果集进行合并,一般会先迭代稀疏的bitset,因为会过滤大量的文档。

  4. es会根据该索引最近的所查询的历史状态,如果该该查询在最近的的256次查询被用到过,则缓存到内存中。如果可以则会判断该索引所在的段是否‘足够大’, 因为比较小的段最后会被合并,分配缓存会被浪费,如果所在的段较小,该缓存会被忽略。

  注意:

  1. 缓存中的bitset是增量更新的,如果新增加一个文档,并不会对所有的bitset缓存进行重新计算,而是只会计算该新文档,然后将结果保存到bitset数组中。

  2. bitset并不会依赖他所在查询的上下文,这样使得缓存可以加速查询中经常使用的部分,从而降低较少、易变的部分所带来的消耗。

    如下图所示:会查询在收件箱且没有读过的,或者不在收件箱的。其中1 和2的 bitset相同,尽管一个是 must ,一个是 must_not
            "bool": {
                 "should": [
                    { "bool": {
                          "must": [
                             { "term": { "folder": "inbox" }},     1
                             { "term": { "read": false }}
                          ]
                     }},
                  { "bool": {
                         "must_not": {
                             "term": { "folder": "inbox" }          2  
                          }
                  }}
                 ]
              }

  3. es会基于使用频率自动缓存查询,如果一个非评分查询在最近的256次查询中被使用过,且所在的段足够大,才会缓存。

你可能感兴趣的:(es深入搜索之结构化搜索)