Elasticsearch查询方式整理之一

结构化搜索

在结构化查询中,我们得到的结果 总是 非是即否,要么存于集合之中,要么存在集合之外。结构化查询不关心文件的相关度或评分;它简单的对文档包括或排除处理。

这在逻辑上是能说通的,因为一个数字不能比其他数字 更 适合存于某个相同范围。结果只能是:存于范围之中,抑或反之。同样,对于结构化文本来说,一个值要么相等,要么不等。没有 更似 这种概念。

精确值查找

当进行精确值查找时,我们会使用过滤器(filters)。过滤器很重要,因为它们执行速度非常快,不会计算相关度(直接跳过了整个评分阶段)而且很容易被缓存。请尽可能多的使用过滤式查询。

term查询数字

一般sql表达

select document from products where price = 20

对应的term搜索

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

通常当查找一个精确值的时候,我们不希望对查询进行评分计算。只希望对文档进行包括或排除的计算,所以我们会使用 constant_score 查询以非评分模式来执行 term 查询并以一作为统一评分。
最终组合的结果是一个 constant_score 查询,它包含一个 term 查询:

GET /my_store/products/_search
{
    "query" : {
        "constant_score" : { 
            "filter" : {
                "term" : { 
                    "price" : 20
                }
            }
        }
    }
}

1、查询置于 filter 语句内不进行评分或相关度的计算,所以所有的结果都会返回一个默认评分 1
2、我们用 constant_score 将 term 查询转化成为过滤器

对于not_analyzed的字段,term查询不会对其进行任何分析,比如分词

组合过滤器

将两个 term 过滤器置入 bool 过滤器的 should 语句内,再增加一个语句处理 NOT 非的条件:

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

注意,我们仍然需要一个 filtered 查询将所有的东西包起来。

查找多个精确值

term 查询对于查找单个值非常有用,但通常我们可能想搜索多个值。 不需要使用多个 term 查询,我们只要用单个 terms 查询(注意末尾的 s ), terms 查询好比是 term 查询的复数形式(以英语名词的单复数做比)。

它几乎与 term 的使用方式一模一样,与指定单个价格不同,我们只要将 term 字段的值改为数组即可:

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

范围

"range" : {
    "price" : {
        "gte" : 20,
        "lte" : 40
    }
}

日期范围

字符串范围

按照字典序排列,但是效率可能比较低下,最好不用

处理null值

存在查询

第一件武器就是 exists 存在查询,这个查询会返回那些在指定字段有任何值的文档。

GET /my_index/posts/_search
{
    "query" : {
        "constant_score" : {
            "filter" : {
                "exists" : { "field" : "tags" }
            }
        }
    }
}

缺失查询

这个 missing 查询本质上与 exists 恰好相反:它返回某个特定 值字段的文档。

GET /my_index/posts/_search
{
    "query" : {
        "constant_score" : {
            "filter": {
                "missing" : { "field" : "tags" }
            }
        }
    }
}

缓存

Elasticsearch 的较早版本中,默认的行为是缓存一切可以缓存的对象。这也通常意味着系统缓存 bitsets 太富侵略性,从而因为清理缓存带来性能压力。不仅如此,尽管很多过滤器都很容易被评价,但本质上是慢于缓存的(以及从缓存中复用)。缓存这些过滤器的意义不大,因为可以简单地再次执行过滤器。

检查一个倒排是非常快的,然后绝大多数查询组件却很少使用它。例如 term 过滤字段 "user_id" :如果有上百万的用户,每个具体的用户 ID 出现的概率都很小。那么为这个过滤器缓存 bitsets 就不是很合算,因为缓存的结果很可能在重用之前就被剔除了。

这种缓存的扰动对性能有着严重的影响。更严重的是,它让开发者难以区分有良好表现的缓存以及无用缓存。

为了解决问题,Elasticsearch 会基于使用频次自动缓存查询。如果一个非评分查询在最近的 256 次查询中被使用过(次数取决于查询类型),那么这个查询就会作为缓存的候选。但是,并不是所有的片段都能保证缓存 bitset 。只有那些文档数量超过 10,000 (或超过总文档数量的 3% )才会缓存 bitset 。因为小的片段可以很快的进行搜索和合并,这里缓存的意义不大。

一旦缓存了,非评分计算的 bitset 会一直驻留在缓存中直到它被剔除。剔除规则是基于 LRU 的:一旦缓存满了,最近最少使用的过滤器会被剔除。

全文搜索

基于词项于基于全文

基于词项的查询

如 term 或 fuzzy 这样的底层查询不需要分析阶段,它们对单个词项进行操作。用 term 查询词项 Foo 只要在倒排索引中查找 准确词项 ,并且用 TF/IDF 算法为每个包含该词项的文档计算相关度评分 _score 。

记住 term 查询只对倒排索引的词项精确匹配,这点很重要,它不会对词的多样性进行处理(如, foo 或 FOO )。这里,无须考虑词项是如何存入索引的。如果是将 ["Foo","Bar"] 索引存入一个不分析的( not_analyzed )包含精确值的字段,或者将 Foo Bar 索引到一个带有 whitespace 空格分析器的字段,两者的结果都会是在倒排索引中有 Foo 和 Bar 这两个词

基于全文的查询

matchquery_string 这样的查询是高层查询,它们了解字段映射的信息:

  • 如果查询 日期(date)整数(integer) 字段,它们会将查询字符串分别作为日期或整数对待。
  • 如果查询一个( not_analyzed )未分析的精确值字符串字段,它们会将整个查询字符串作为单个词项对待。
  • 但如果要查询一个( analyzed )已分析的全文字段,它们会先将查询字符串传递到一个合适的分析器,然后生成一个供查询的词项列表。

一旦组成了词项列表,这个查询会对每个词项逐一执行底层的查询,再将结果合并,然后为每个文档生成一个最终的相关度评分。

我们很少直接使用基于词项的搜索,通常情况下都是对全文进行查询,而非单个词项,这只需要简单的执行一个高层全文查询(进而在高层查询内部会以基于词项的底层查询完成搜索)。

匹配查询

匹配查询 match 是个 核心 查询。无论需要查询什么字段, match 查询都应该会是首选的查询方式。它是一个高级 全文查询 ,这表示它既能处理全文字段,又能处理精确字段。

这就是说, match 查询主要的应用场景就是进行全文搜索

单个词查询

我们用第一个示例来解释使用 match 查询搜索全文字段中的单个词:

GET /my_index/my_type/_search
{
    "query": {
        "match": {
            "title": "QUICK!"
        }
    }
}

Elasticsearch 执行上面这个 match 查询的步骤是:

  1. 检查字段类型 。
    标题 title 字段是一个 string 类型( analyzed )已分析的全文字段,这意味着查询字符串本身也应该被分析。

  2. 分析查询字符串 。
    将查询的字符串 QUICK! 传入标准分析器中,输出的结果是单个项 quick 。因为只有一个单词项,所以 match 查询执行的是单个底层 term 查询。

  3. 查找匹配文档 。
    用 term 查询在倒排索引中查找 quick 然后获取一组包含该项的文档,本例的结果是文档:1、2 和 3 。

  4. 为每个文档评分 。
    用 term 查询计算每个文档相关度评分 _score ,这是种将 词频(term frequency,即词 quick 在相关文档的 title 字段中出现的频率)和反向文档频率(inverse document frequency,即词 quick 在所有文档的 title 字段中出现的频率),以及字段的长度(即字段越短相关度越高)相结合的计算方式。

多词查询

GET /my_index/my_type/_search
{
    "query": {
        "match": {
            "title": "BROWN DOG!"
        }
    }
}

match 查询必须查找两个词( ["brown","dog"] ),它在内部实际上先执行两次 term 查询,然后将两次查询的结果合并作为最终结果输出。为了做到这点,它将两个 term 查询包入一个 bool 查询中,详细信息见 布尔查询。

以上示例告诉我们一个重要信息:即任何文档只要 title 字段里包含 指定词项中的至少一个词 就能匹配,被匹配的词项越多,文档就越相关。

提高精度

任意 查询词项匹配文档可能会导致结果中出现不相关的长尾。这是种散弹式搜索。可能我们只想搜索包含 所有 词项的文档,也就是说,不去匹配 brown OR dog,而通过匹配 brown AND dog 找到所有文档。

match 查询还可以接受 operator 操作符作为输入参数,默认情况下该操作符是 or 。我们可以将它修改成 and 让所有指定词项都必须匹配。

控制精度

所有任意 间二选一有点过于非黑即白。 如果用户给定 5 个查询词项,想查找只包含其中 4 个的文档,该如何处理?将 operator 操作符参数设置成 and 只会将此文档排除。

有时候这正是我们期望的,但在全文搜索的大多数应用场景下,我们既想包含那些可能相关的文档,同时又排除那些不太相关的。换句话说,我们想要处于中间某种结果。

match 查询支持 minimum_should_match 最小匹配参数, 这让我们可以指定必须匹配的词项数用来表示一个文档是否相关。我们可以将其设置为某个具体数字,更常用的做法是将其设置为一个百分数,因为我们无法控制用户搜索时输入的单词数量。

你可能感兴趣的:(Elasticsearch查询方式整理之一)