【Elasticsearch】自定义评分检索
自定义评分检索
- 1.自定义评分
- 2.为什么需要自定义评分
- 3.搜索结果相关度
- 4.影响相关度评分的查询子句
- 5.控制相关度评分的方法
- 5.1 Function Score Query
- 5.1.1 基础查询部分
- 5.1.2 评分函数部分(functions 数组)
- 第一个函数:品牌加权
- 第二个函数:销量因子
- 第三个函数:时间衰减
- 5.1.3 评分组合方式
- score_mode
- boost_mode
- 5.1.4 整体效果
- 5.2 使用 Boosting Query
- 5.2.1 正向查询(positive)
- 5.2.2 负向查询(negative)
- 5.2.3 降权系数(negative_boost)
- 5.3.4 工作流程
- 5.2.5 与 bool 查询的区别
- 5.2.6 典型应用场景
- 5.2.7 参数注意事项
- 5.2.8 性能考虑
- 5.3 使用 Script Score
- 6.相关度评分控制技巧
1.自定义评分
自定义评分(Score Customization
)是指通过编程方式修改或覆盖 Elasticsearch 默认的相关度评分算法,以满足特定业务需求的过程。
Elasticsearch 默认使用 TF-IDF
(在较新版本中使用 BM25
)算法计算文档相关性,但有时业务需求需要特殊的排序逻辑。
🚀 建议先看一下我的这几篇博文:
- 【自然语言处理】文本表示:One-Hot、BOW、TF-IDF、N-Gram
- 【自然语言处理】BOW 和 TF-IDF 详解
- 【自然语言处理】文本相似度算法:TF-IDF 与 BM25
2.为什么需要自定义评分
- 业务特定需求:默认评分可能无法准确反映业务领域的相关性标准。
- 个性化排序:根据用户偏好、地理位置、时间敏感性等因素调整结果。
- 商业逻辑:如提升付费内容、促销商品的排名。
- 多维度排序:结合相关性、销量、评分、新鲜度等多个因素。
- 领域专业知识:某些领域有独特的价值评估标准。
3.搜索结果相关度
- 相关度(
_score
)是 Elasticsearch 判断文档与查询匹配程度的量化值。 - 自定义评分 不是替代相关度计算,而是在其基础上进行 调整或补充。
- 最终排序可以结合默认相关度和自定义评分因素。
4.影响相关度评分的查询子句
- 全文检索查询:
match
、match_phrase
、multi_match
、query_string
- 词项级别查询:
term
(通常不评分,但可通过参数启用)、terms
、fuzzy
- 复合查询:
bool
(must
/should
)、dis_max
、constant_score
- 特殊查询:
boosting
、function_score
、script_score
- 查询参数:
boost
(可应用于任何查询)、tie_breaker
(在dis_max
中使用)
filter
和 must_not
被视为过滤器,不影响评分。其他查询子句会影响评分。
5.控制相关度评分的方法
接下来,我们就介绍一下 boosting
、function_score
、script_score
这几个特殊查询是如何影响评分的。
5.1 Function Score Query
{"query": {"function_score": {"query": { "match": { "title": "手机" } },"functions": [{"filter": { "term": { "brand": "苹果" } },"weight": 2},{"field_value_factor": {"field": "sales","modifier": "log1p","factor": 0.1}},{"gauss": {"date": {"origin": "now","scale": "30d","offset": "7d","decay": 0.5}}}],"score_mode": "sum","boost_mode": "multiply"}}
}
这段代码是一个 Elasticsearch 的 function_score
查询,用于对基础查询结果进行自定义评分调整。这是一个复合查询,主要包含:
- 基础查询(
match
查询) - 多个评分函数(
functions
数组) - 评分组合方式(
score_mode
和boost_mode
)
5.1.1 基础查询部分
"query": { "match": { "title": "手机" } }
- 这是基础查询,查找
title
字段包含 “手机
” 的文档。 - 这部分会先执行,产生初始的相关性评分(
_score
)。
5.1.2 评分函数部分(functions 数组)
第一个函数:品牌加权
{"filter": { "term": { "brand": "苹果" } },"weight": 2
}
- 作用:对品牌为 “
苹果
” 的文档应用权重。 - 机制:
- 先过滤出
brand
字段精确匹配 “苹果
” 的文档。 - 为这些文档的评分乘以权重因子 2。
- 先过滤出
- 效果:苹果品牌的产品会获得更高的排名。
第二个函数:销量因子
{"field_value_factor": {"field": "sales","modifier": "log1p","factor": 0.1}
}
- 作用:根据销量(
sales
字段)调整评分。 - 参数:
field
:使用sales
字段的值。modifier
:应用log1p
函数(即log(1 + sales)
),防止极高销量值对评分影响过大。factor
:乘以0.1
的因子,减小影响程度。
- 效果:销量高的产品会获得一定提升,但不会完全主导排序。
第三个函数:时间衰减
{"gauss": {"date": {"origin": "now","scale": "30d","offset": "7d","decay": 0.5}}
}
- 作用:基于日期字段(
date
)实现时间衰减评分。 - 高斯衰减参数:
origin
:当前时间("now"
)作为基准点。scale
:衰减范围为 30 30 30 天。offset
: 7 7 7 天内不衰减,保持完整评分。decay
:衰减到 0.5 0.5 0.5(在origin
+offset
+scale
时,得分为0.5*原始分
)。
- 效果:较新的文档会获得更高评分,但 7 7 7 天内无差别,之后逐渐衰减。
5.1.3 评分组合方式
score_mode
"score_mode": "sum"
- 指定多个函数评分如何组合。
sum
表示将所有函数评分相加。- 其他可选值:
multiply
(乘)、avg
(平均)、max
(最大)、min
(最小)、first
(第一个函数)。
boost_mode
"boost_mode": "multiply"
- 指定函数评分如何与原始查询评分组合。
multiply
表示将函数评分与原始_score
相乘。- 其他可选值:
replace
(替换)、sum
(相加)、avg
(平均)、max
(最大)、min
(最小)。
5.1.4 整体效果
这个查询会:
- 首先找出所有标题包含 “
手机
” 的文档。 - 然后对这些文档进行评分调整:
- 如果是
苹果
品牌,评分×2
。 - 根据销量(
取对数后
)增加评分。 - 根据时间远近衰减评分(越新评分越高)。
- 如果是
- 将所有函数评分相加(
score_mode: sum
)。 - 最后将总函数评分与原始评分相乘(
boost_mode: multiply
)。 - 按最终评分排序返回结果。
这种查询非常适合电商搜索场景,可以同时考虑关键词相关性、品牌偏好、销量热度和新品时效性。
5.2 使用 Boosting Query
{"query": {"boosting": {"positive": { "match": { "title": "手机" } },"negative": { "term": { "brand": "山寨" } },"negative_boost": 0.2}}
}
这段代码使用了 Elasticsearch 的 boosting
查询,它是一种特殊的复合查询,用于对匹配某些条件的文档进行降权而非完全排除。下面我将详细解释每个部分的含义和作用:
positive
:正向查询(必须匹配)negative
:负向查询(匹配则降权)negative_boost
:降权系数
5.2.1 正向查询(positive)
"positive": { "match": { "title": "手机" } }
- 这是一个标准的全文匹配查询。
- 会找出所有
title
字段包含 “手机
” 的文档。 - 这些文档都会被包含在最终结果中(如果不匹配
negative
条件则保持原评分)。
5.2.2 负向查询(negative)
"negative": { "term": { "brand": "山寨" } }
- 这是一个精确匹配查询(
term query
)。 - 会找出
brand
字段精确等于 “山寨
” 的文档。 - 匹配
negative
查询的文档不会被排除,而是会被降权。
5.2.3 降权系数(negative_boost)
"negative_boost": 0.2
- 这是一个介于 0 0 0 和 1 1 1 之间的乘数因子。
- 对于 同时匹配 positive 和 negative 的文档:
最终评分 = 原始评分 × 0.2
- 对于 只匹配 positive 的文档:
保持原始评分不变
5.3.4 工作流程
- 首先执行
positive
查询,找出所有标题包含 “手机
” 的文档。 - 然后在这些文档中,找出品牌为 “
山寨
” 的文档。 - 对匹配
negative
条件的文档,将其评分乘以0.2
。 - 最后按调整后的评分排序返回结果。
5.2.5 与 bool 查询的区别
特性 | | must_not ) |
---|---|---|
匹配 negative 的文档 | 仍会返回,但评分降低 | 完全排除 |
适用场景 | 需要展示但降权低质量内容 | 需要完全排除某些内容 |
评分影响 | 可控的降权(通过 negative_boost ) | 完全不影响评分(只是过滤) |
5.2.6 典型应用场景
- 降权低质量内容:如示例中的山寨品牌。
- 处理过时内容:降权旧文章但不完全排除。
- 广告混合排序:对广告内容适当降权。
- 用户偏好处理:对用户不喜欢的类型降权。
5.2.7 参数注意事项
-
negative_boost
必须是 0 0 0 到 1 1 1 之间的浮点数:- 0.2 0.2 0.2 表示降权到原评分的 20 % 20\% 20%。
- 1.0 1.0 1.0 相当于没有效果。
- 0.0 0.0 0.0 相当于完全排除(此时应使用
bool
+must_not
)。
-
可以组合多个
positive
和negative
条件:"positive": {"bool": {"should": [{ "match": { "title": "手机" } },{ "match": { "description": "智能" } }]} }, "negative": {"bool": {"should": [{ "term": { "brand": "山寨" } },{ "range": { "price": { "lt": 100 } } }]} }
5.2.8 性能考虑
- 比纯过滤(
bool
+must_not
)消耗更多资源,因为要计算被降权文档的评分。 - 对于需要完全排除的大量文档,建议使用
bool
+must_not
过滤。 - 适合处理少量需要降权而非完全排除的文档。
5.3 使用 Script Score
{"query": {"script_score": {"query": { "match": { "title": "手机" } },"script": {"source": "_score * doc['popularity'].value * params.weight","params": { "weight": 1.5 }}}}
}
- 首先执行基础
match
查询,找出所有标题包含 “手机
” 的文档,并计算原始_score
。 - 对每个匹配的文档:
- 获取其
popularity
字段的值。 - 使用脚本计算新评分:
新评分 = _score × popularity × 1.5
。
- 获取其
- 按照计算出的新评分对文档进行排序。
- 返回结果。
6.相关度评分控制技巧
- 理解评分因素
- 词频(
Term Frequency
) - 逆文档频率(
Inverse Document Frequency
) - 字段长度归一值(
Field-length norm
)
- 词频(
- 使用 Explain API 分析评分
GET /products/_search {"explain": true,"query": { "match": { "title": "手机" } } }
- 调整相似度算法
- 可配置 BM25 参数(
k1
,b
)。 - 或完全自定义相似度实现。
- 可配置 BM25 参数(
- 查询结构调整
- 合理使用
bool
查询的should
/must
/filter
。 - 调整不同子句的
boost
值。
- 合理使用
自定义评分是 Elasticsearch 强大的功能之一,合理使用可以显著提升搜索体验,但也需要谨慎设计以避免性能问题和不可预期的排序结果。