1. BM25 算法解析:Elasticsearch 的默认相关性评分机制
BM25 (Best Match 25) 是 Elasticsearch 默认使用的相关性评分算法,是 BM 算法的改进版本。BM25 基于概率检索模型,通过计算查询词在文档中的出现频率与文档长度之间的关系来评估文档相关性。
BM25 的核心公式为:
score(D,Q) = ∑ IDF(q_i) * (f(q_i) * (k1 + 1)) / (f(q_i) + k1 * (1 - b + b * |D|/avgdl))
其中:
IDF(q_i)是查询词q_i的逆文档频率f(q_i)是词q_i在文档 D 中的出现频率|D|是文档 D 的长度avgdl是平均文档长度k1和b是调节参数,Elasticsearch 默认值分别为 1.2 和 0.75
BM25 的主要优势在于平衡了词频和文档长度对相关性的影响,避免了传统 TF-IDF 算法中长文档获得不公平高评分的问题。
在 Elasticsearch 中,我们可以通过查询 API 直接使用 BM25 算法:
GET /my_index/_search
{
"query": {
"match": {
"content": "elasticsearch search algorithm"
}
}
}
BM25 算法的参数可以通过 similarities 设置进行调整:
PUT /my_index
{
"settings": {
"similarity": {
"default": {
"type": "BM25",
"k1": 1.2,
"b": 0.75
}
}
}
}
2. Function Score 扩展:灵活调整文档评分的方法
虽然 BM25 提供了基础的评分机制,但在实际业务场景中,我们可能需要根据特定规则调整文档的相关性评分。Elasticsearch 的 Function Score 允许我们在查询过程中对文档进行二次评分和调整。
Function Score 的基本结构如下:
GET /my_index/_search
{
"query": {
"function_score": {
"query": {
"match": {
"content": "elasticsearch"
}
},
"functions": [
{
"filter": { "term": { "category": "tech" } },
"weight": 2
},
{
"filter": { "range": { "date": { "gte": "now-30d" } } },
"weight": 1.5
}
],
"score_mode": "sum",
"boost_mode": "multiply"
}
}
}
Function Score 支持多种函数类型:
| 函数类型 | 功能描述 | 适用场景 |
|---|---|---|
| weight | 为匹配的文档设置固定权重 | 基于业务规则的简单加权 |
| random_score | 为每个文档分配随机分数 | 打破固定排序,增加多样性 |
| fieldValueFactor | 基于文档字段值调整分数 | 利用数值字段影响评分 |
| decay functions | 基于距离或时间衰减 | 地理位置或时效性相关场景 |
| script_score | 使用自定义脚本计算分数 | 复杂业务逻辑评分 |
Function Score 通过 score_mode 和 boost_mode 参数控制如何组合多个函数的结果:
score_mode: 定义如何组合多个函数的分数(sum, multiply, max, min, replace)boost_mode: 定义如何将函数结果与原始查询分数结合(multiply, replace, sum, avg, max, min)
3. 自定义评分模型:Script Score 与高级评分策略
当 Function Score 提供的函数无法满足复杂业务需求时,Elasticsearch 的 Script Score 允许我们使用自定义脚本实现更灵活的评分策略。
Script Score 的基本语法:
GET /my_index/_search
{
"query": {
"function_score": {
"query": { "match_all": {} },
"functions": [
{
"script_score": {
"script": {
"source": "doc['rating'].value * doc['review_count'].value / 1000"
}
}
}
]
}
}
}
Elasticsearch 支持多种脚本语言(Painless, JavaScript, 等),其中 Painless 是 Elasticsearch 5.0+ 默认且推荐使用的脚本语言。
自定义评分模型的设计应遵循以下原则:
- 明确业务目标:确定评分的主要目的是什么(相关性、时效性、质量等)
- 平衡性能与精度:复杂脚本可能影响查询性能,需要权衡
- 考虑缓存策略:避免频繁访问磁盘字段,利用文档缓存
- 测试与迭代:通过日志分析评分效果,持续优化
4. 实战案例:综合应用评分优化搜索体验
下面是一个综合应用 BM25、Function Score 和 Script Score 的实战案例,实现一个电商产品搜索的相关性优化:
业务场景:
一个电商平台需要根据用户搜索"笔记本电脑"返回最相关的产品,同时考虑以下因素:
- 产品名称和描述与搜索词的匹配度(BM25)
- 产品类别相关性(Function Score)
- 产品评分和销量(Script Score)
- 是否有货(Function Score)
- 上架时间(Function Score)
实现方案:
GET /products/_search
{
"query": {
"function_score": {
"query": {
"multi_match": {
"query": "笔记本电脑",
"fields": [
"name^4",
"description^2",
"category^3",
"brand^2"
],
"type": "best_fields",
"minimum_should_match": "30%"
}
},
"functions": [
{
"filter": { "term": { "category": "computer" } },
"weight": 2
},
{
"filter": { "term": { "category": "electronics" } },
"weight": 1.5
},
{
"filter": { "term": { "in_stock": true } },
"weight": 1.8
},
{
"field_value_factor": {
"field": "rating",
"modifier": "log1p",
"factor": 1.5,
"missing": 1
}
},
{
"gauss": {
"上架时间": {
"origin": "now",
"scale": "30d",
"offset": "7d",
"decay": 0.5
}
}
}
],
"score_mode": "sum",
"boost_mode": "multiply",
"max_boost": 10
}
},
"size": 20
}
效果分析:
通过上述评分策略,我们实现了:
- 基础相关性通过 BM25 算法保障,产品名称匹配权重最高
- 类别相关性通过 Function Score 加权,计算机类别产品优先
- 评分通过 field_value_factor 加权,高质量产品优先
- 时效性通过 gauss 函数调节,近期上架产品获得更高权重
5. 最佳实践与注意事项
在使用 Elasticsearch 相关性评分时,需要注意以下事项:
- 参数调优:BM25 的
k1和b参数应根据数据特点调整,通常k1在 1.2-2.0 之间,b在 0.75 左右 - 性能监控:复杂函数和脚本可能影响查询性能,应使用 profile API 分析查询成本
- 避免过度优化:简单的相关性问题可能不需要自定义评分,BM25 和 Function Score 通常已足够
- 版本兼容:不同版本的 Elasticsearch 在脚本支持和语法上可能有差异
- 测试与验证:使用相关性评估工具验证评分策略效果
Elasticsearch 相关性评分处理流程图:
最小示例代码
// 基础 BM25 查询
GET /products/_search
{
"query": {
"match": {
"description": "laptop computer"
}
}
}
// 使用 Function Score 调整
GET /products/_search
{
"query": {
"function_score": {
"query": {
"match": {
"description": "laptop computer"
}
},
"functions": [
{
"filter": { "term": { "in_stock": true } },
"weight": 1.5
}
],
"boost_mode": "multiply"
}
}
}
// 使用 Script Score 完全自定义
GET /products/_search
{
"query": {
"function_score": {
"query": { "match_all": {} },
"functions": [
{
"script_score": {
"script": {
"source": "
double score = 0;
score += Math.min(doc['description.keyword'].value.length(), 200) / 200;
score += doc['rating'].value / 5;
score += Math.log(doc['sales_count'].value + 1) / 10;
return score;
",
"lang": "painless"
}
}
}
],
"boost_mode": "replace"
}
}
}
转载自 CSDN-专业IT技术社区
原文链接:https://blog.csdn.net/qq_41840843/article/details/164985045




