
自定义评分检索
- 1.自定义评分
- 2.为什么需要自定义评分
- 3.搜索结果相关度
- 4.影响相关度评分的查询子句
- 5.控制相关度评分的方法
-
- [5.1 Function Score Query](#5.1 Function Score Query)
-
- [5.1.1 基础查询部分](#5.1.1 基础查询部分)
- [5.1.2 评分函数部分(functions 数组)](#5.1.2 评分函数部分(functions 数组))
- [5.1.3 评分组合方式](#5.1.3 评分组合方式)
- [5.1.4 整体效果](#5.1.4 整体效果)
- [5.2 使用 Boosting Query](#5.2 使用 Boosting Query)
-
- [5.2.1 正向查询(positive)](#5.2.1 正向查询(positive))
- [5.2.2 负向查询(negative)](#5.2.2 负向查询(negative))
- [5.2.3 降权系数(negative_boost)](#5.2.3 降权系数(negative_boost))
- [5.3.4 工作流程](#5.3.4 工作流程)
- [5.2.5 与 bool 查询的区别](#5.2.5 与 bool 查询的区别)
- [5.2.6 典型应用场景](#5.2.6 典型应用场景)
- [5.2.7 参数注意事项](#5.2.7 参数注意事项)
- [5.2.8 性能考虑](#5.2.8 性能考虑)
- [5.3 使用 Script Score](#5.3 使用 Script Score)
- 6.相关度评分控制技巧
1.自定义评分
自定义评分(Score Customization)是指通过编程方式修改或覆盖 Elasticsearch 默认的相关度评分算法,以满足特定业务需求的过程。
Elasticsearch 默认使用 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
json
{
"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 基础查询部分
json
"query": { "match": { "title": "手机" } }
- 这是基础查询,查找
title字段包含 "手机" 的文档。 - 这部分会先执行,产生初始的相关性评分(
_score)。
5.1.2 评分函数部分(functions 数组)
第一个函数:品牌加权
json
{
"filter": { "term": { "brand": "苹果" } },
"weight": 2
}
- 作用 :对品牌为 "
苹果" 的文档应用权重。 - 机制 :
- 先过滤出
brand字段精确匹配 "苹果" 的文档。 - 为这些文档的评分乘以权重因子 2。
- 先过滤出
- 效果:苹果品牌的产品会获得更高的排名。
第二个函数:销量因子
json
{
"field_value_factor": {
"field": "sales",
"modifier": "log1p",
"factor": 0.1
}
}
- 作用 :根据销量(
sales字段)调整评分。 - 参数 :
field:使用sales字段的值。modifier:应用log1p函数(即log(1 + sales)),防止极高销量值对评分影响过大。factor:乘以0.1的因子,减小影响程度。
- 效果:销量高的产品会获得一定提升,但不会完全主导排序。
第三个函数:时间衰减
json
{
"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
json
"score_mode": "sum"
- 指定多个函数评分如何组合。
sum表示将所有函数评分相加。- 其他可选值:
multiply(乘 )、avg(平均 )、max(最大 )、min(最小 )、first(第一个函数)。
boost_mode
json
"boost_mode": "multiply"
- 指定函数评分如何与原始查询评分组合。
multiply表示将函数评分与原始_score相乘。- 其他可选值:
replace(替换 )、sum(相加 )、avg(平均 )、max(最大 )、min(最小)。
5.1.4 整体效果
这个查询会:
- 首先找出所有标题包含 "
手机" 的文档。 - 然后对这些文档进行评分调整:
- 如果是
苹果品牌,评分×2。 - 根据销量(
取对数后)增加评分。 - 根据时间远近衰减评分(越新评分越高)。
- 如果是
- 将所有函数评分相加(
score_mode: sum)。 - 最后将总函数评分与原始评分相乘(
boost_mode: multiply)。 - 按最终评分排序返回结果。
这种查询非常适合电商搜索场景,可以同时考虑关键词相关性、品牌偏好、销量热度和新品时效性。
5.2 使用 Boosting Query
json
{
"query": {
"boosting": {
"positive": { "match": { "title": "手机" } },
"negative": { "term": { "brand": "山寨" } },
"negative_boost": 0.2
}
}
}
这段代码使用了 Elasticsearch 的 boosting 查询,它是一种特殊的复合查询,用于对匹配某些条件的文档进行降权而非完全排除。下面我将详细解释每个部分的含义和作用:
positive:正向查询(必须匹配)negative:负向查询(匹配则降权)negative_boost:降权系数
5.2.1 正向查询(positive)
json
"positive": { "match": { "title": "手机" } }
- 这是一个标准的全文匹配查询。
- 会找出所有
title字段包含 "手机" 的文档。 - 这些文档都会被包含在最终结果中 (如果不匹配
negative条件则保持原评分)。
5.2.2 负向查询(negative)
json
"negative": { "term": { "brand": "山寨" } }
- 这是一个精确匹配查询(
term query)。 - 会找出
brand字段精确等于 "山寨" 的文档。 - 匹配
negative查询的文档不会被排除,而是会被降权。
5.2.3 降权系数(negative_boost)
json
"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 查询的区别
| 特性 | boosting 查询 | 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条件:json"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
json
{
"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 分析评分
jsonGET /products/_search { "explain": true, "query": { "match": { "title": "手机" } } } -
调整相似度算法
- 可配置 BM25 参数(
k1,b)。 - 或完全自定义相似度实现。
- 可配置 BM25 参数(
-
查询结构调整
- 合理使用
bool查询的should/must/filter。 - 调整不同子句的
boost值。
- 合理使用
自定义评分是 Elasticsearch 强大的功能之一,合理使用可以显著提升搜索体验,但也需要谨慎设计以避免性能问题和不可预期的排序结果。