Elasticsearch 相关性控制的艺术
我们可以通过控制相关性的得分使得用户最想要的结果排在最前面。本文所举的例子依旧是建设博客的搜索引擎。
1. boosting 查询
它允许我们为一个查询指定的一个“正面”条件和一个“负面”条件,匹配到负面条件的文档,其相关性得分会按照比例降低
需求:搜索“Elasticsearch”,但如果文章标签中包含“过时”,则降低其排名
GET /blog_posts/_search
{
"query": {
"boosting": {
"positive": { "match": { "content": "Elasticsearch" } },
"negative": { "term": { "tags": "过时" } },
"negative_boost": 0.2 // 负面匹配的文档,得分将乘以 0.2
}
}
}
2. function_score
这是最强大的相关性控制工具,允许我们用一个或多个函数来修改原始查询的 _score。
需求:搜索“Java”,并综合考虑浏览量,评论数,点赞数和发布时间来决定最终排名
GET /blog_posts/_search
{
"query": {
"function_score": {
"query": { "match": { "content": "Java" } },
"functions": [
{ "filter": { "range": { "view_count": { "gte": 1000 } } }, "weight": 1.5 }, // 浏览量过千,权重乘以1.5
{ "field_value_factor": { "field": "comment_count", "modifier": "log1p", "factor": 0.5 } }, // 评论数越多得分越高
{ "field_value_factor": { "field": "likes", "modifier": "log1p", "factor": 0.3 } }, // 点赞越多得分越高
{ "gauss": { "publish_date": { "origin": "now", "scale": "90d", "decay": 0.5 } } } // 离现在越近得分越高
],
"score_mode": "sum", // 将所有函数得分与原始得分相加
"boost_mode": "multiply" // 将最终计算出的函数总分与原始得分相乘
}
}
}
"modifier": "log1p"
:这是平滑处理的关键。直接用评论数(比如10000)可能会导致分数过高,压倒其他因素。log1p
函数(计算log(1 + x)
)可以削弱极端值的影响,使得评论数从10增加到100带来的分数提升,远大于从10000增加到10100带来的提升。这更符合“边际效益递减”的直觉。"gauss"
(高斯衰减函数):这是一个“时效性/距离衰减器”。它根据一个数值或日期字段的值与一个“中心点”的接近程度来打分。"origin": "now"
:设置“中心点”为当前时间,如果发布日期就是今天,得分就会最高为1。"scale": "90d"
:定义了衰减的速率。它设定了一个关键的校准点:当文章的发布日期距离现在达到90天时,它的时效性得分会正好衰减为下面decay
定义的值。"decay": 0.5
:定义衰减率。当距离达到scale
(90天)时,分数会衰减为decay
的值,即 0.5。
"score_mode"
: 决定如何将functions
数组中每个函数独立计算出的分数合并成一个总的函数分,可选模式:sum
(求和):总函数分 = 分数1 + 分数2 + 分数3multiply
(求积):总函数分 = 分数1 * 分数2 * 分数3avg
(平均值)、max
(最大值)、min
(最小值)
"boost_mode"
:决定如何将上一步计算出的**“总函数分”应用到第一步得到的“原始_score
”**上,从而得到最终排名分,可选模式:multiply
(相乘):最终分 = 原始_score
* 总函数分。这是最常用的,因为它能让函数分作为原始相关性的一个“增强系数”sum
(相加):最终分 = 原始_score
+ 总函数分replace
(替换):最终分 = 总函数分 (完全忽略原始的_score
)avg
,max
,min
3. rescore 查询
当我们有一个计算开销非常大的排序逻辑时,rescore 是最佳选择。它只对第一阶段查询返回的 top N 个结果,进行第二次、更复杂的打分。
需求:先快速地从百万篇文章中找出包含“性能优化”的 100 篇文章,然后只对这 100 篇文章,运行一个复杂的、基于脚本的业务排序逻辑。
GET /blog_posts/_search
{
"query": { "match": { "content": "性能优化" } },
"rescore": {
"window_size": 100, // 只对前100个结果进行二次打分
"query": {
"rescore_query": {
"function_score": { // 在这里使用复杂的 function_score
// ... 一个计算开销很大的函数 ...
}
}
}
}
}
评论区
请登录后发表评论