从查询重写角度理解elasticsearch的高亮原理

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从查询重写角度理解elasticsearch的高亮原理相关的知识,希望对你有一定的参考价值。

参考技术A 一、高亮的一些问题

elasticsearch提供了三种高亮方式,前面我们已经简单的了解了elasticsearch的高亮原理; 高亮处理跟实际使用查询类型有十分紧密的关系,其中主要的一点就是muti term 查询的重写,例如wildcard、prefix等,由于查询本身和高亮都涉及到查询语句的重写,如果两者之间的重写机制不同,那么就可能会碰到以下情况

相同的查询语句, 使用unified和fvh得到的高亮结果是不同的,甚至fvh Highlighter无任何高亮信息返回;

二、数据环境

elasticsearch 8.0

三、muti term查询重写简介

所谓muti term查询就是查询中并不是明确的关键字,而是需要elasticsearch重写查询语句,进一步明确关键字;以下查询会涉及到muti term查询重写;

以上查询都支持rewrite参数,最终将查询重写为bool查询或者bitset;

查询重写主要影响以下几方面

重写需要抓取哪些关键字以及抓取的数量;

抓取关键字的相关性计算方式;

查询重写支持以下参数选项

constant_score,默认值,如果需要抓取的关键字比较少,则重写为bool查询,否则抓取所有的关键字并重写为bitset;直接使用boost参数作为文档score,一般term level的查询的boost默认值为1;

constant_score_boolean,将查询重写为bool查询,并使用boost参数作为文档的score,受到indices.query.bool.max_clause_count 限制,所以默认最多抓取1024个关键字;

scoring_boolean,将查询重写为bool查询,并计算文档的相对权重,受到indices.query.bool.max_clause_count 限制,所以默认最多抓取1024个关键字;

top_terms_blended_freqs_N,抓取得分最高的前N个关键字,并将查询重写为bool查询;此选项不受indices.query.bool.max_clause_count 限制;选择命中文档的所有关键字中权重最大的作为文档的score;

top_terms_boost_N,抓取得分最高的前N个关键字,并将查询重写为bool查询;此选项不受indices.query.bool.max_clause_count 限制;直接使用boost作为文档的score;

top_terms_N,抓取得分最高的前N个关键字,并将查询重写为bool查询;此选项不受indices.query.bool.max_clause_count 限制;计算命中文档的相对权重作为评分;

三、wildcard查询重写分析

我们通过elasticsearch来查看一下以下查询语句的重写逻辑;

通过查询使用的字段映射类型构建WildCardQuery,并使用查询语句中配置的rewrite对应的MultiTermQuery.RewriteMethod;

根据查询语句中配置的rewrite,查找对应的MultiTermQuery.RewriteMethod,由于我们没有在wildcard查询语句中设置rewrite参数,这里直接返回null;

WildCardQuery继承MultiTermQuery,直接调用rewrite方法进行重写,由于我们没有在wildcard查询语句中设置rewrite参数,这里直接使用默认的CONSTANT_SCORE_REWRITE;

可以看到CONSTANT_SCORE_REWRITE是直接使用的匿名类,rewrite方法返回的是MultiTermQueryConstantScoreWrapper的实例;

在以下方法中,首先会得到查询字段对应的所有term集合;
然后通过 query.getTermsEnum获取跟查询匹配的所有term集合;
最后根据collectTerms调用的返回值决定是否构建bool查询还是bit set;

调用collectTerms默认只会提取查询命中的16个关键字;

通过以上分析wildcard查询默认情况下,会提取字段中所有命中查询的关键字;

四、fvh Highlighter中wildcard的查询重写

在muti term query中,提取查询关键字是高亮逻辑一个很重要的步骤;

我们使用以下高亮语句,分析以下高亮中提取查询关键字过程中的查询重写;

默认情况下只有匹配的字段才会进行高亮,这里构建CustomFieldQuery;

通过调用flatten方法得到重写之后的flatQueries,然后将每个提取的关键字重写为BoostQuery;

由于WildCardQuery是MultiTermQuery的子类,所以在flatten方法中最终直接使用MultiTermQuery.TopTermsScoringBooleanQueryRewrite进行查询重写,这里的top N是MAX_MTQ_TERMS = 1024;

这里首先计算设置的size和getMaxSize(默认值1024, IndexSearcher.getMaxClauseCount())计算最终提取的命中关键字数量,这里最终是1024个;

这里省略了传入collectTerms的TermCollector匿名子类的实现,其余最终提取关键字数量有关;

这里首先获取查询字段对应的所有term集合,然后获取所有的与查询匹配的term集合,最终通过传入的collector提取关键字;

这里通过控制最终提取匹配查询的关键字的数量不超过maxSize;

通过以上分析可以看到,fvh Highlighter对multi term query的重写,直接使用MultiTermQuery.TopTermsScoringBooleanQueryRewrite,并限制只能最多提取查询关键字1024个;

五、重写可能导致的高亮问题原因分析

经过以上对查询和高亮的重写过程分析可以知道,默认情况下

query阶段提取的是命中查询的所有的关键字,具体行为可以通过rewrite参数进行定制;

Highlight阶段提取的是命中查询的关键字中的前1024个,具体行为不受rewrite参数的控制;

如果查询的字段是大文本字段,导致字段的关键字很多,就可能会出现查询命中的文档的关键字不在前1024个里边,从而导致明明匹配了文档,但是却没有返回高亮信息;

六、解决方案

Elasticsearch--搜索

目录

基本知识

查询结果返回设置:版本值、得分限制、定制返回字段

{
    "version":true,
    "min_score":0.75,
    "fields":["a", "b"],
    "query":{
        ...
    }
}

定制字段的返回,从性能角度考虑,不如直接返回_source

搜索类型

通过search_type设置,请求类型是:

  1. query_then_fetch:默认设置。第一步在所有分片上执行查询得到对文档进行排序和分级所需信息,然后,在相关分片上查询文档的实际内容。该查询类型返回结果的最大数量等于size参数的值。
  2. query_and_fetch:最快最简单的搜索类型,所有分片并行查询,每个分片返回等于size值得结果数。
  3. dfs_query_and_fetch:相比query_and_fetch多包含一个额外阶段,在初始查询中执行分布式词频计算,返回的得分更加精确。
  4. dfs_query_then_fetch:相比query_then_fetch多包含一个额外阶段,在初始查询中执行分布式词频计算,返回的得分更加精确。
  5. count:只返回匹配的文档的数量,不返回文档
  6. scan:遍历查询,不计算得分。

搜索执行偏好

参数值 描述
_primary 只在主分片上执行搜索,不使用副本。
_primary_first 如果主分片可用,只在主分片上搜索,否则在其他分片执行
_local 只在发送请求的节点上的可用分片上执行搜索
_only_node:node_id 只在提供标示符的节点上搜索
_prefer_node:node_id Elasticsearch尝试在提供标示符的节点上搜索,否则,使用其他节点
_shards:1,2 在提供标示符的分片上执行,可以和其他首选项合并,但_shards必须在前。_shards:1,2;_local
自定义值 传入自定义的字符串

基本查询

  1. 词条查询(term)
  2. 多词条查询,通过mininum_match指定最小匹配的词条数量
  3. match_all查询
  4. query_string查询。支持全部的Apache Lucene查询语法
  5. 标示符查询。针对内部的_id字段
  6. more_like_this查询
  7. 最大分查询(dismax)。它非常有用,会生成一个由所有子查询返回的文档组成的并集。我们可以控制较低得分的子查询对文档最后得分的影响。最后得分是这样计算的:最高分数的子查询的得分之和,加上其余子查询的得分之和乘以tie参数的值,通过tie参数控制较低得分的查询对最后得分的影响
  8. 加权查询(boosting)。该查询封装了两个查询,有三个节点需要定义:positive部分包含所返回文档得分不会被改变;negative部分返回的文档得分将被降低;negative_boost部分包含降低negative部分查询得分的加权值
  9. constant_score查询。封装一个查询,被封装查询返回文档得分设置成一个常数

此外,elasticsearch还支持:前缀查询,模糊查询,通配符查询,正则查询

词条和多词条查询elasticsearch没有分析过程。

过滤器类型

  1. 范围过滤器
  2. exists过滤器
  3. missing过滤器
  4. 脚本过滤器
  5. 类型过滤器,指的是索引的类型
  6. 限定过滤器:限定单个分片返回的文档数目
  7. 标示符过滤器
  8. 命名过滤器。可以给过滤器指定名称,这样当过滤器很复杂时,就可以在查看返回结果中某一文档匹配了哪些过滤器

过滤器缓存:有些过滤器是默认缓存的,有些过滤器则不能缓存(即使打开也是无效的)。通过_cache参数设置

高亮

Lucene提供了三种类型的高亮实现:标准类型;FastVectorHighlighter,它需要词向量和位置才能工作;PostingsHighlighter,信息高亮器(字段定义中的index_options属性设为offsets),对单个词条的查询更智能,匹配句子结束边界。

Elasticsearch会自动选择正确的高亮实现方式,如果字段的配置中,term_vector属性设成了with_positings_offsets,则将使用FastVectorHighlighter。使用词向量将导致索引变大,但高亮执行时间更少,对于存储大量数据的字段来说,推荐使用FastVectorHighlighter

高亮的所有设置既可以在所有字段上设置,也可以针对某个字段单独设置(全局控制和局部控制)。

require_field_match控制高亮的字段只与查询时指定的字段对应,不会显示其他字段的符合条件的高亮信息。

控制高亮的片段

number_of_fragments:返回高亮片段的数量,默认是5。如果设置为0,整个字段将会返回,对于短字段可以这样设置。

fragment_size:高亮片段的最大字符长度,默认值是100

验证查询

ES查询时把请求发送到_search端点,有时候不知道DSL或定义语句是否正确时,将请求发送到_validate_query端点验证请求的正确性(有的错误请求无返回结果,这时无法判断是没有符合要求的数据还是请求错误)。
验证的命令如下

curl -XGET ‘localhost:9200/index/_validate/query?pretty&explain‘ --data-binary @query.json

query.json是命令文件

数据排序

默认的排序是根据得分降序排序,可以通过{"sort":{"_sort":asc}}对结果升序排序

还可以指定字段进行排序,这个字段最好不要分析。

通过这查询中设置missing可以指定缺失字段在查询结果中的排列位置。

查询重写

任何涉及多词条的查询,如前缀查询或通配符查询,都会使用查询重写。它把原始的昂贵的查询修改成一组不太昂贵的查询。

Lucene内部的倒排索引机制记录了词条,对查询进行重写的时候,将条件匹配相关的词条,从而改写查询。

{
    "query":{
        "prefix":{
            "title":"s",
            "rewrite":"constant_score_boolean"
        }
    }
}

rewrite指定了重写的策略:

  • scoring_boolean:该选项将每个生成的词项转化为布尔查询中的一个或从句(Boolean should clause)。这种改写方法需要针对每个文档都计算得分。因此,这种方法比较耗费CPU,而且有些查询生成了太多的词项,以至于超出了布尔查询默认的1024个从句的限制。默认的布尔查询限制可以通过设置Elasticsearch.yml文件的index.query.bool.max_clause_count属性来修改。改写后的布尔查询的从句数越多,查询性能越低
  • constant_score_boolean:该选项与前面提到过的scoring_boolean类似,但是CPU耗费更少,这是因为并不计算每个从句的得分,而是每个从句得到一个与查询权重相同的一个常数得分,默认情况下等于1,我们也可以通过设置查询权重来改变这个默认值。与scoring_boolean类似,该选项也有布尔从句数的限制。
  • constant_score_filter(默认选项):在匹配的词项较少时,与constant_score_boolean类似,匹配的词项较多时,会遍历并查询所有的词项,然后使用相同的常数得分。文档数量较多时,好于上面两个。
  • top_terms_N:与scoring_boolean相似之处在于都会对从句计算得分,不同之处在于,该方法只保留最佳的N个词项,以避免触及布尔从句数的限制,并提升查询整体性能。
  • top_terms_boost_N:该选项与top_terms_N类似,不同之处在于它的文档得分不是通过计算得出的,而是被设置为跟查询权重(boost)一致,默认值为1。

需要性能更好,但忍受低精度选用topN,需要高精度,但忍受低性能选择constant_score_boolean


以上是关于从查询重写角度理解elasticsearch的高亮原理的主要内容,如果未能解决你的问题,请参考以下文章

Elasticsearch之高亮查询,聚合查询,

Elasticsearch之高亮查询,聚合查询,

ElasticSearch多个字段分词查询高亮显示

Elasticsearch-高亮查询

ElasticSearch分布式搜索引擎从入门到实战应用(实战篇-仿京东首页搜索商品高亮显示)

ElasticSearch实战(二十八)-高亮查询