从查询重写角度理解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设置,请求类型是:
- query_then_fetch:默认设置。第一步在所有分片上执行查询得到对文档进行排序和分级所需信息,然后,在相关分片上查询文档的实际内容。该查询类型返回结果的最大数量等于size参数的值。
- query_and_fetch:最快最简单的搜索类型,所有分片并行查询,每个分片返回等于size值得结果数。
- dfs_query_and_fetch:相比query_and_fetch多包含一个额外阶段,在初始查询中执行分布式词频计算,返回的得分更加精确。
- dfs_query_then_fetch:相比query_then_fetch多包含一个额外阶段,在初始查询中执行分布式词频计算,返回的得分更加精确。
- count:只返回匹配的文档的数量,不返回文档
- scan:遍历查询,不计算得分。
搜索执行偏好
参数值 | 描述 |
---|---|
_primary | 只在主分片上执行搜索,不使用副本。 |
_primary_first | 如果主分片可用,只在主分片上搜索,否则在其他分片执行 |
_local | 只在发送请求的节点上的可用分片上执行搜索 |
_only_node:node_id | 只在提供标示符的节点上搜索 |
_prefer_node:node_id | Elasticsearch尝试在提供标示符的节点上搜索,否则,使用其他节点 |
_shards:1,2 | 在提供标示符的分片上执行,可以和其他首选项合并,但_shards必须在前。_shards:1,2;_local |
自定义值 | 传入自定义的字符串 |
基本查询
- 词条查询(term)
- 多词条查询,通过mininum_match指定最小匹配的词条数量
- match_all查询
- query_string查询。支持全部的Apache Lucene查询语法
- 标示符查询。针对内部的_id字段
- more_like_this查询
- 最大分查询(dismax)。它非常有用,会生成一个由所有子查询返回的文档组成的并集。我们可以控制较低得分的子查询对文档最后得分的影响。最后得分是这样计算的:最高分数的子查询的得分之和,加上其余子查询的得分之和乘以tie参数的值,通过tie参数控制较低得分的查询对最后得分的影响
- 加权查询(boosting)。该查询封装了两个查询,有三个节点需要定义:positive部分包含所返回文档得分不会被改变;negative部分返回的文档得分将被降低;negative_boost部分包含降低negative部分查询得分的加权值
- constant_score查询。封装一个查询,被封装查询返回文档得分设置成一个常数
此外,elasticsearch还支持:前缀查询,模糊查询,通配符查询,正则查询
词条和多词条查询elasticsearch没有分析过程。
过滤器类型
- 范围过滤器
- exists过滤器
- missing过滤器
- 脚本过滤器
- 类型过滤器,指的是索引的类型
- 限定过滤器:限定单个分片返回的文档数目
- 标示符过滤器
- 命名过滤器。可以给过滤器指定名称,这样当过滤器很复杂时,就可以在查看返回结果中某一文档匹配了哪些过滤器
过滤器缓存:有些过滤器是默认缓存的,有些过滤器则不能缓存(即使打开也是无效的)。通过_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的高亮原理的主要内容,如果未能解决你的问题,请参考以下文章