乐趣区

关于elasticsearch:总是搜不到想要的内容Elasticsearch搜索排名优化了解一下

导语 | Elasticsearch(下文简称 ES)是以后热门的开源全文搜索引擎,利用它咱们能够方便快捷搭建出搜寻平台,但通用的配置还须要依据平台内容的具体情况做进一步优化,能力产生令用户称心的搜寻后果。下文将介绍对 ES 搜寻排名的优化实际,心愿与大家一起交换。文章作者:曹毅,腾讯利用开发工程师。

一、引言

尽管应用 ES 能够十分不便疾速地搭建出搜寻平台,但搜进去的后果往往不合乎预期。因为 ES 是一个通用的全文搜索引擎,它无奈了解被搜寻的内容,通用的配置也无奈适宜所有内容的搜寻。所以 ES 在搜寻中的利用须要针对具体的平台做很多的优化才能够达到良好的成果。

ES 搜寻后果排序是 通过 query 关键字与文档内容计算相关性评分 来实现的。想把握相关性评分并不容易。首先 ES 对中文并不是很敌对,须要装置插件与做一些预处理,其次影响相关性评分的因素比拟多,这些因素可控性高,灵活性高。

下文将为大家介绍 ES 搜寻排名优化上的实践经验,本篇文章示例索引数据来自一份报告文档,如下图所示:

二、优化 ES Query DSL

构建完搜寻平台后,咱们首先要进行 ES Query DSL 的优化。针对 ES 的优化,关键点就在于优化 Query DSL,只有 DSL 应用失当,搜寻的最终成果就会很好。

1. 最后应用的 multi_match

当咱们构建好索引同步好数据当前,想 比拟快实现全文索引的形式就是应用 multi_match 来查问,例如:

这样应用十分不便和疾速,并且实现了全文搜寻的需要。然而搜寻的后果可能不太合乎预期。然而不必放心,咱们能够持续往下优化。

2. 应用 bool 查问的 filter 减少筛选

在利用中,咱们应该防止间接让用户针对所有内容进行查问,这样会返回大量的命中后果,如果后果的排序略微有一点出入,用户将无奈获取到更精准的内容。

针对这种状况,咱们能够 给内容减少一些标签、分类等筛选项 提供给用户做抉择,以达到更好的后果排名。这样搜寻时被 ES 引擎评分的指标后果将会变少,评分的抖动影响会更小。

实现这个性能就应用到 bool 查问的过滤器。bool 查问中提供了 4 个语句,must / filter / should / must_not,其中 filter / must_not 属于过滤器,must / should 属于查询器。对于过滤器,你须要晓得以下两点:

  • 过滤器并不计算相关性评分,因为被过滤掉的内容不会影响返回内容的排序;
  • 过滤器能够应用 ES 外部的缓存,所以过滤器能够进步查问速度。

这里须要留神:尽管 must 查问像是一种正向过滤器,然而它所查问的后果将会返回并会和其余的查问一起计算相关性评分,因而无奈应用缓存,与过滤器并不一样。

个别一个文档领有多个能够被筛选的属性,例如 id、工夫、标签、分类等。为了搜寻的品质咱们应该认真地对文档进行打标签和分类解决,因为一旦抉择了过滤,即便用户的搜寻关键词再匹配文档也不会被返回了。示例 Query DSL 如下:

下面的示例中,存在一个小技巧,即应用标签的 id 来进行筛选。因为 tags 字段是 text 类型的,term 查问是准确匹配,不要将其利用到 text 类型的字段上 ,如果 text 字段要被过滤器应用,在 mappings 中应该要应用 string 类型(它将字段映射到两个类型上,text 和 keyword) 或者 keyword 类型。

3. 应用 match_phrase 进步搜寻短语的权重

在这个阶段,搜寻的时候常常会呈现 搜寻后果和搜寻关键词不是间断匹配的 状况。例如搜寻关键词为:“2020 年微信用户钻研报告”,而返回的后果大多数是匹配“微信”、“用户”、“钻研”、“报告”这些零散的关键词,而用户想要匹配整个短语的后果却在前面。

这并不是 ES 的 bug,在理解这种行为之前,咱们须要先弄清楚 ES 是如何解决 match 的?

首先 multi_match 会把多个字段的匹配转换成多个 match 查问组合,挨个对字段进行 match 查问。match 执行查问时,先把查问关键词通过 search_analyzer 设置的分析器剖析,再把分析器失去的后果挨个放进 bool 查问中的 should 语句,这些 should 没有权重与程序的差异,并且只有命中一个 should 语句的文档都会被返回。转换语句如下图所示,后面是原语句,前面是转换后的语句:

这样就导致了有的文档只领有查问短语中若干个词,但评分却比能够匹配整个短语的文档高的状况。那咱们如何思考词的程序呢?先别急,咱们再来看看 ES 中的倒排索引。

咱们都晓得倒排索引中记录了一个词到蕴含词文档的 ID,但倒排索引当然不会这么简略。倒排列表中记录了单词对应的文档汇合,由倒排索引项组成。倒排索引项中次要蕴含如下信息:

  • 文档 ID:用于获取文档;
  • 单词词频(TF):用于相关性计算(TF-IDF,BM25);
  • 地位:记录单词在文档中的分词地位,会有多个,用于短语查问;
  • 偏移:记录在文档中的开始地位与完结地位,用于高亮。

这下咱们就很分明了,ES 专门记录了词语的地位信息用于查问,在 DSL 中是应用 match_phrase 查问 。match_phrase 要求必须命中所有分词,并且返回的文档命中的词也要依照查问短语的程序, 词的间距能够应用 slop 设置

match_phrase 尽管帮咱们解决了程序的问题,然而它要求比拟刻薄,须要命中所有分词。如果独自应用它来进行搜寻,会发现搜寻进去的后果相比 match 会大大减少,这是因为匹配若干个词的文档和匹配程序不对的文档都没被返回。

这时候能够采纳 bool 查问的 should 语句,同时应用 match 与 match_phrase 查问语句,这样相当于 match_pharse 进步了搜寻短语程序的权重,使得可能程序匹配到的文档相关性评分更高。如下是示例 DSL:

这里有一点须要留神,在倒排索引项中 text  类型的数组里,每个元素记录的地位是间断的。例如某文档数据中的 tags:[“ 腾讯 CDC”,” 京东研究院”],“CDC”与“京东”的地位是间断的,如果搜寻“CDC 京东”,那此文档的评分将会比拟高。

这种状况是不应该产生的,咱们应该在设置索引 mappings 时,给 tags 字段设置上 position_increment_gap,来减少数组元素之间的地位,此地位要超过查问所应用的 slop,例如:

4. 应用 boost 调整查问语句的权重

前文提到的搜寻实现,有一个不言而喻的问题:所有字段都无权重之分。依据常识咱们晓得,title 的权重应该高于其余字段,显然不能和其余字段是一样的得分。

查问时能够用 boost 配置来减少权重 ,不过这里设置的对象并不是某个字段,而是查问语句。设置后,查问语句的得分等于 默认得分乘以 boost

设置 boost 有几个须要留神的中央:

  • 数据品质高的字段能够相应进步权重;
  • match_phrase 语句的权重应该高于相应字段 match 查问的权重,因为文档中按程序匹配的短语可能数量不会太多,然而查问关键词被分词后的词语将会很多,match 的得分将会比拟高,则 match 的得分将会冲淡 match_phrase 的影响;
  • 在 mappings 设置中,能够针对字段设置权重,查问时不必再针对字段应用 boost 设置。

具体示例 DSL 如下:

5. 应用 function_score 减少更多的评分因素

影响文档评分的还有一些因素,例如我可能会常常思考以下问题:

  • 工夫越近的文档信息比拟及时,对用户更有用,应该排在后面;
  • 平台中热门的文档,可能用户比拟喜爱,应该比其余文档好;
  • 文档品质比拟高的,更心愿让用户看到,那些缺失标签与摘要的文档并不心愿用户总是看到;
  • 经营人员有时候想让用户搜到正在推广的文档;
  • ……

咱们能够通过减少更多的影响报告评分的因素来实现以上场景,这些因素包含:工夫、热度、品质评分、经营权重等。

这些因素有一个特点,就是在数据搭建阶段咱们就能确定其权重,并且和查问关键词没有什么关系。这些文档自身就具备的权重属性咱们能够认为是动态评分,须要和查问关键词来计算出的相关性评分称为动静评分,所以一个文档的最终评分应该是动静评分与动态评分的联合。

动态评分相干的属性不应该轻易设置。为了给用户一个更好的体验,动态评分的影响应该具备:

  • 稳定性:不要常常有大幅度的变动,如果大幅度变动会导致用户搜寻雷同的关键词过段时间进去的后果会不同;
  • 连续性:不便咱们其余的优化也能影响总评分,例如对于热度 0.1 与 1000 的文档,即便用户搜寻 0.1 热度文档的匹配度为 1000 热度文档的 100 倍,但后果排名仍然比不过 1000 热度的文档;
  • 区分度:在间断稳固的状况下,应该有肯定的区分度,也即分值的距离应该正当。如果有 1000 份文档,在 1.0 分到 1.001 分之间,这其实是没有实际意义的,因为对文档排名的影响太少了。

新减少的这些因素并没有太通用的查问语句,不过 ES 提供了 function_score 来让咱们自定义评分计算公式,也提供了多种类型不便咱们疾速利用。function_score 提供了五种类型

  • script_score,这是最灵便的形式,能够自定义算法;
  • weight,乘以一个权重数值;
  • random_score,随机分数;
  • field_value_factor,应用某个字段来影响总分数;
  • decay fucntion,包含 gauss、exp、linear 三种衰减函数。

因为类型比拟多,上面只介绍应用较多的 filed_value_factor 与 decay function 的理论案例。

(1)filed_value_factor

热度、举荐权重等对评分的影响能够按权重相乘,刚好适宜 filed_value_factor 这种类型的函数,实现如下:

下面这段 DSL 的含意是:sqrt (1.2 * doc[‘likes’].value),如果缺失了此字段则为 1。missing 这里的设置有个小技巧,比方假设缺失摘要的文档为品质低的文档,能够适当升高权重,那咱们能够把 missing 设置为小于 1 的数值,factor 填 1 即可。

(2)decay function

衰减函数是一个十分有用的函数,它能够帮咱们实现平滑过渡,使间隔某个点越近的文档分数越高,越远的分数越低。应用衰减函数很容易实现工夫越近的文档得分就越高的场景。

ES 提供了三个衰减函数,咱们先来看一下这三种衰减函数的差异,截取官网文档展现图如下:

  • linear,是两条线性函数,从直线和横轴相交处以外,评分都为 0;
  • exp,是指数函数,先激烈的衰减,而后迟缓衰减;
  • guass,高斯衰减是最罕用的,先迟缓再激烈再迟缓,scale 相交的点左近衰减比拟激烈。

当咱们想选取肯定范畴内的后果,或者肯定范畴内的后果比拟重要时,例如某个工夫、地区(圆形)、价格范畴内,都能够应用高斯衰减函数。高斯衰减函数有 4 个参数能够设置

  • origin:中心点,或字段可能的最佳值,落在原点 origin 上的文档评分 _score 为满分 1.0;
  • scale:衰减率,即一个文档从原点 origin 着落时,评分 _score 扭转的速度;
  • decay:从原点 origin 衰减到 scale 所得的评分 _score,默认值为 0.5;
  • offset:以原点 origin 为中心点,为其设置一个非零的偏移量 offset 笼罩一个范畴,而不只是单个原点。在范畴 -offset <= origin <= +offset 内的所有评分 _score 都是 1.0。

假设搜索引擎中三年内的文档会比拟重要,三年之前的信息价值升高,就能够抉择 origin 为明天,scale 为三年,decay 为 0.5,offset 为三个月,DSL 如下:

6. 最终后果

到这里咱们的 Query DSL 曾经优化的差不多了,当初的搜寻后果终于能够让人称心了。咱们来看一下最终的 DSL 语句示例:(示例并非理论运行的代码)

三、优化相关性算法

上文咱们探讨了相关性评分应该由动静评分和动态评分联合得出,动态评分咱们曾经有了优化的办法,下文咱们再来讨论一下动静评分的计算。

所谓动静评分,就是用户每次查问都要计算用户查问关键词与文档的相关性,更细一点来说,就是 实时计算全文搜寻字段的相关性

ES 提供了一种十分好的形式,实现了可插拔式的配置,容许咱们管制每个字段的相关性算法。在 Mappings 设置阶段,咱们 能够调整 similarity 的参数并给不同的字段设置不同的 similarity 来达到调整相关性算法的目标。ES 提供了几种可用的 similarity,咱们接下来次要探讨 BM 25。

ES 默认的相关性算法就是 BM25,它是一个 基于概率模型的词与文档的相关性算法,能够看做是基于向量空间模型的 TF-IDF 算法的降级。ES 在 7.0.0 版本曾经废除了 TF-IDF 算法,齐全应用 BM25 替换,BM25 与 TF-IDF 算法的比照和细节本文不形容。

先来看看 wikipedia 上 BM25 的公式:

一眼看上去有这么多变量,是不是感觉难以了解?不过不必放心,咱们须要调整的参数其实只有两个。这些变量里除了 k1 与 b,其余都是能够间接从文档中算出的,所以在 ES 中 BM25 公式其实就是靠调整这两个参数来影响评分。

k1 这个参数管制着词频后果在词频饱和度中的回升速度。默认值为 1.2。值越小饱和度变动越快,值越大饱和度变动越慢。词频饱和度能够参看上面官网文档的截图,图中反馈了词频对应的得分曲线,k1 管制 tf of BM25 这条曲线。

b 这个参数管制着字段长归一值所起的作用,0.0 会禁用归一化,1.0 会启用齐全归一化。默认值为 0.75。

在优化 BM25 的 k1 和 b 时,咱们要 依据搜寻内容的特点动手,仔细分析检索的需要

例如在示例的索引数据中 content 字段的品质参差不齐,甚至有些文档可能会缺失此字段,但此文档对应的实在数据(可能是某文件、某视频等)品质很高,因而放入 ES 中 content 字段的长度并不能反映文档实在的状况,更不心愿 content 短的文档被突出,所以咱们要 弱化文档长度对评分的影响

依据 k1 和 b 的形容,咱们将 BM25 模型中的 b 值从默认的 0.75 升高,具体升高到多少才适合,还须要进一步的尝试。这里我以调整到 0.2 为例,写出对应的 settings 和 mappings:

k1 和 b 的默认值实用于绝大多数文档汇合,但最优值还是会因为文档集不同而有所区别,为了找到文档汇合的最优值,就必须对参数进行重复批改验证。

四、优化的倡议

对 ES 搜寻的优化应该把大部分精力花在文档数据品质晋升和查问 DSL 组合调优上,须要重复尝试各种查问的组合和调整权重,在 DSL 的优化曾经达到较好水平之前,尽量不要调整 similarity。

更不要在初期就引入太多的插件,例如近义词,拼音等,这样会影响你的优化,它们只是进步搜寻召回率的工具,并不一定会进步准确率。更业余的平台应该做好更业余的搜寻疏导与倡议,而不是让用户自觉的去尝试搜寻

搜寻的调优也不能始终关注技术方面,还要关注用户 。搜寻品质的好坏是一个比拟主观的评估,想要理解用户是否称心搜寻后果,只能通过 监测搜寻后果和用户的行为,例如用户反复搜寻的频率,翻页的频次等。

如果搜寻能返回相关性较高的文档,用户应该会在第一次搜寻便失去想要的内容,如果返回相关性不太好的后果,用户可能会来回点击并尝试新的搜寻条件。

五、应用 _explain 做 bad case 剖析

到这里,咱们的搜寻排名优化就告一段落,但可能时不时还会有一些用户反馈搜寻的后果不精确。尽管有可能是用户本身不会应用搜索引擎(这里应该从产品上疏导用户写出更好的查问关键词),但更多时候应该还是排名优化没有做好。

所以如果发现了 bad case,咱们应该记录这些 bad case,从这些问题动手仔细分析问题出在哪里,而后缓缓优化。

剖析 bad case 能够应用 ES 提供的 _explain 查问 api,它会返回咱们应用某 DSL 查到某文档的得分细节,通过这些细节,咱们就能晓得产生 bad case 的起因,而后有针对性的下手优化了。

六、结语

ES 是一个通用型的全文检索引擎,如果想用 ES 搭建一个业余的搜寻平台,必须要通过搜寻调优才能够达到可用的状态。本文总结了基于 ES 的搜寻优化,次要是优化 DSL 与相关性计算,心愿读者能够从中学习到有用的常识。

看腾讯技术,学云计算常识,来云 + 社区:https://cloud.tencent.com/developer

退出移动版