Elasticsearch系列(12)Query之复合查询

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Elasticsearch系列(12)Query之复合查询相关的知识,希望对你有一定的参考价值。

参考技术A Elasticsearch提供了一个完整的基于JSON的查询DSL(领域特定语言)来定义查询。可以将查询DSL看作查询的AST(抽象语法树),它由两种类型的子句(clauses)组成:
(1)叶查询子句(Leaf query clauses):特定字段中查找特定值,例如匹配(match)、词条(term)或范围(range)查询。这些查询可以单独查询使用。
(2)复合查询子句(Compound query clauses):包装其他叶查询或复合查询,并使用逻辑方式来组合多个查询(如bool或dis_max查询),或用于改变它们的行为(如constant_score查询)。

查询子句的行为取决于它们是在查询上下文中使用还是在筛选上下文中使用。

某些类型的查询通常执行缓慢,这是因为它们的实现方式会影响集群的稳定性,这些查询分类如下:

可以通过设置参数allow_expensive_queries为false(默认为true)来阻止此类查询的执行。

默认情况下,Elasticsearch根据相关性评分对匹配的搜索结果进行排序,相关性评分衡量每个文档与查询的匹配程度。相关性得分是一个正浮点数,在搜索API的_score元数据字段中返回。_score字段值越高,文档越相关。

在查询(query)上下文中,一个查询子句回答了问题“这个文档与这个查询子句匹配得有多好?”,除了决定文档是否匹配之外,查询子句还会计算相关性分数保存在_score元数据字段中。

在过滤器(filter)上下文中,查询子句回答问题“这个文档与这个查询子句匹配吗?”,答案是简单的“是”或“不是”—没有计算分数。过滤器上下文主要用于过滤结构化数据。

注释1:query参数表示查询上下文
注释2:在查询上下文中使用bool和两个match子句,这意味着它们会对每个文档的匹配程度进行评分。
注释3:filter参数表示过滤器上下文,在过滤器上下文中使用term和range子句,它们将过滤掉不匹配的文档,但不会影响匹配文档的得分。

注意:在查询上下文中为查询计算的分数表示为单精度浮点数;它们只有24位的精度。超过精度将被转换为浮点数进行分数计算,从而丢失精度。

首先创建索引my_index_01,并索引数据:

用于组合多个叶子或复合查询子句的布尔值查询,使用一个或多个布尔子句构建的,每个子句都有一个有类型的,如must、should、must_not或filter子句。

查询示例如下:

使用minimum_should_match参数来指定返回的文档必须匹配的should子句的数量或百分比。如果布尔查询包含should子句,并且没有must或filter子句,则默认值为1,否则默认值为0。

每个查询可在其顶级定义一个_name。可以使用命名查询来跟踪匹配返回的文档。如果使用了命名查询,则响应对于每次命中都包含一个matched_queries属性。示例如下:

返回结果如下

boosting查询包含两部分:positive查询和negative查询。

示例如下:

返回结果片段如下:

包装一个过滤器(filter)查询,并返回每个文档的相关性分数为常量boost参数值。

示例如下:

dis_max查询包含一个或多个查询子句,如果返回的文档匹配了多个查询子句,dis_max查询将找到匹配子句中最高相关性分数, 将其分配给文档,而且为任何其他匹配子查询加上一个离线增量。

示例如下:

返回结果片段如下:

function_score查询可以修改查询返回的文档相关性分数,需要定义一个查询和若干个函数,用这些函数为查询返回的每个文档计算一个新分数。

示例如下:

除了自定义函数,还可以使用脚本函数,使用script_score函数可以包装另一个查询,并使用脚本表达式从文档中的其他数值字段值来定制它的评分。示例如下:

脚本支持参数形式,示例如下:

Elasticsearch Query DSL 整理总结—— Match Phrase Query 和 Match Phrase Prefix Query

目录

引言

今天再读庄子的《逍遥游》,其中鲲鹏之扶摇直上九万里之气势,蜩(tiao)与学鸠之渺小之对比,令人印象深刻,并对鲲鹏之志心生向往。而郭象在注《庄子》卷中却说,"苟足于其性,则虽大鹏无以自贵于小鸟,小鸟无羡于天池,而荣愿有余矣。故小大虽殊,逍遥一也。"观看自身,虽然不是什么领导,老总,但也完全不必感到为职业生涯忧虑,只要热爱程序员这个工作,享受编码的乐趣,做到 80 岁又有何妨。

书归正传,今天我们聊聊 Match Phase Query。

Match Phase Query

match_phrase 查询针对的是一个语句,比如 "like football", 分析时也会将整个语句作为整体,而不会像上篇的 match 查询 会将整个语句拆分为单个词条。

举个例子,创建一个 match_phase type 并塞进去一个文档, message 是 I like swimming and riding!

PUT matchphasetest
{}

PUT matchphasetest/_mapping/match_phase
{
  "properties": {
    "message": {
      "type": "text"
    }
  }
}

PUT matchphasetest/match_phase/1
{
  "message": "I like swimming and riding!"
}

GET matchphasetest/_search
{
  "query": {
    "match_phrase": {
      "message": "I like swimming"
    }
  }
}

默认使用 match_phrase 时会精确匹配查询的短语,需要全部单词和顺序要完全一样,标点符号除外。

slop 参数

这种精确匹配在大部分情况下显得太严苛了,有时我们想要包含 ""I like swimming and riding!"" 的文档也能够匹配 "I like riding"。这时就要以用到 "slop" 参数来控制查询语句的灵活度。

slop 参数告诉 match_phrase 查询词条相隔多远时仍然能将文档视为匹配 什么是相隔多远? 意思是说为了让查询和文档匹配你需要移动词条多少次?

以 "I like swimming and riding!" 的文档为例,想匹配 "I like riding",只需要将 "riding" 词条向前移动两次,因此设置 slop 参数值为 2, 就可以匹配到。

GET matchphasetest/_search
{
  "query": {
    "match_phrase": {
      "message": {
        "query": "I like riding",
        "slop": 2
      }
    }
  }
}

analyzer 参数

match_phrase 语句也可以设置 analyzer 参数来定义查询语句时对其中词条执行的分析过程。

默认情况下,使用的是创建 mapping 时的分析器,如果没有指定就会使用默认的查询分析器。这里举个例子(只是如何使用)

GET /_search
{
    "query": {
        "match_phrase" : {
            "message" : {
                "query" : "this is a test",
                "analyzer" : "my_analyzer"
            }
        }
    }
}

zero terms query

match_phrase 也接受 zero_terms_query 为参数,使用方式和 match查询语句相同

Match Phrase 前缀查询

match_phrase_prefixmatch_phrase 用法是一样的,区别就在于它允许对最后一个词条前缀匹配。以上节的数据为例,查询 I like sw 就能匹配到

I like swimming and riding

GET matchphasetest/_search
{
  "query": {
    "match_phrase_prefix": {
      "message": "I like swi"
    }
  }
}

max_expansions

官方文档中说 match_phrase_prefix 查询中有个参数 max_expansions 说的是参数 max_expansions 控制着可以与前缀匹配的词的数量,默认值是 50。

I like swi 查询为例,它会先查找第一个与前缀 swi 匹配的词,然后依次查找搜集与之匹配的词(按字母顺序),直到没有更多可匹配的词或当数量超过 max_expansions 时结束。

但是我在使用时,故意造出了数十个以 swi 开头的词,而将 max_expansions 的值设为 10。但是却返回了所有的结果。在 elasitc 官网也有对该问题的讨论, 也是没有找到答案。这个问题作为一个公案权且记下,如果您知道原因,麻烦告诉我,非常感谢。

这里也贴出个例子,以备后面排查

GET matchphaseprefixtest/_search
{
  "query": {
    "match": {
      "message": {
        "query": "I like sw",
        "max_expansions": 10
       }
    }
  }
}

match_phrase_prefix 用起来非常方便,能够实现输入即搜索的效果,但是也会出现问题。 假如说查询 I like s 并且想要匹配 I like swimming ,结果是默认情况下它会搜索出前 50 个组合,如果前 50 个没有 swimming ,那就不会显示出结果。只能是用户继续输入后面的字母才可能匹配出结果。

要实现更好的即使搜索的特性,可以看看 completion suggester
Index-Time Search-as-You-Type 能不能实现。

小结

本文论述了 Match Phase Query 和 Match Phrase 前缀查询 的使用,下文会讲解 Multi Match Query 敬请期待。

参考文档

1.Match Phrase Query

系列文章列表

Query DSL

  1. Query DSL 概要,MatchAllQuery,全文查询简述
  2. Match Query

Java Rest Client API

  1. Elasticsearch Java Rest Client API 整理总结 (一)——Document API
  2. Elasticsearch Java Rest Client API 整理总结 (二) —— SearchAPI
  3. Elasticsearch Java Rest Client API 整理总结 (三)——Building Queries

以上是关于Elasticsearch系列(12)Query之复合查询的主要内容,如果未能解决你的问题,请参考以下文章

Elasticsearch系列(14)Query之地理空间查询

Elasticsearch Query DSL 整理总结—— Match Phrase Query 和 Match Phrase Prefix Query

Elasticsearch的javaAPI之query dsl-queries

Elasticsearch语法知多少之Match query

Elasticsearch Query DSL之全文检索(Full text queries)下篇

(12)ElasticSearch 基本查询(Query查询)