Elasticsearch 的相关性评分是怎么计算的?BM25 怎么理解?
简化版
Elasticsearch 默认会给匹配文档计算 _score,用来表示文档和查询的相关程度。现代 Elasticsearch 默认相似度算法是 BM25,可以粗略理解为:查询词在文档中出现越多越相关,越稀有的词权重越高,字段越短命中越集中,但词频增长不是无限线性加分。
详细版
相关性评分的目标是把“更像用户想要的结果”排在前面。BM25 主要考虑三类因素:
- TF:词在当前文档中出现的频率。出现次数多,通常更相关。
- IDF:词在整个索引中有多稀有。越少见的词,区分能力越强。
- 字段长度归一化:短字段命中关键词通常比长字段偶然命中更有意义。
例如搜索“Java 并发”:
- 标题是“Java 并发面试题”的文章,通常分数高。
- 正文几万字里只出现一次“并发”的文章,分数可能低。
- “的”“是”这类高频词区分度低,权重不应该高。
实际业务中还可以通过:
- 字段 boost:标题权重大于正文;
function_score:结合热度、时间、新鲜度;minimum_should_match:控制至少匹配多少词;explainAPI:查看评分原因;- 调整 analyzer:提升分词质量。
但面试要强调:评分不是数据库排序那么简单,它依赖倒排索引统计信息、字段配置和查询写法。
完整版教学
一、为什么搜索需要评分
精确查询只需要判断“等不等”,全文检索需要判断“更相关还是不相关”。
例如用户搜索:
Elasticsearch 分片 原理
下面两篇文章都命中关键词:
A:Elasticsearch 分片原理与集群设计
B:常见搜索系统介绍,正文某处提到 Elasticsearch,也提到分片
如果只返回匹配结果,不排序,用户体验会很差。相关性评分就是为了解决排序问题。
二、TF:词出现越多,通常越相关
TF 可以理解为 term frequency。一个查询词在文档里出现多次,说明文档可能更围绕这个主题。
但 BM25 不会让词频无限线性加分。因为一个词出现 100 次并不一定比出现 10 次相关 10 倍,它可能只是堆关键词。BM25 对词频增长做了饱和处理,避免纯堆词把评分冲得过高。
这也是搜索评分和简单计数不同的地方:它既承认词频有价值,也限制词频的过度影响。
三、IDF:越稀有的词越有区分度
IDF 可以理解为 inverse document frequency。一个词如果几乎每篇文档都有,它对区分文档帮助很小;如果只有少量文档包含,它的区分度就很强。
例如在技术文章里:
- “Java”可能很多文档都有;
- “AQS”出现得更少;
- “StampedLock”可能更少。
用户搜“Java StampedLock”时,StampedLock 对排序的影响通常应该比 Java 更明显。
四、字段长度归一化:短字段命中更集中
同样命中“分片原理”,标题只有 10 个词且完整命中,通常比一篇很长正文里偶然出现一次更相关。
这就是字段长度归一化的直觉:
短标题:Elasticsearch 分片原理
长正文:几万字文章里某段提到分片原理
命中密度不同,相关性也不同。
五、业务上如何调相关性
实际搜索通常不只依赖默认评分,还会结合业务权重。
多字段权重:
{
"query": {
"multi_match": {
"query": "Java 并发",
"fields": ["title^3", "summary^2", "content"]
}
}
}
标题权重 ^3 表示标题命中更重要。
结合业务热度:
{
"query": {
"function_score": {
"query": {
"match": { "title": "Elasticsearch" }
},
"field_value_factor": {
"field": "view_count",
"modifier": "log1p",
"factor": 0.2
}
}
}
}
这样可以让“文本相关性”与“业务热度”一起影响排序。但要控制权重,避免热门但不相关的内容压过真正相关的内容。
六、如何排查评分不符合预期
排查相关性问题可以按这个顺序:
- 用
_analyze看查询词和文档字段如何分词。 - 用
explain查看某篇文档的评分细节。 - 检查字段类型是否正确,全文字段是否误用了
keyword。 - 检查查询写法,精确条件是否放进了 filter。
- 检查字段 boost、同义词、停用词、minimum_should_match 是否合理。
- 如果排序还要结合时间、热度、销量,再考虑
function_score或单独排序策略。
七、常见误区与追问
| 因素 | 直觉解释 | 面试表达 |
|---|---|---|
| TF | 文档里出现次数 | 多次出现更相关,但 BM25 会饱和 |
| IDF | 词在全库稀有程度 | 越少见越有区分度 |
| 字段长度 | 命中密度 | 短字段命中通常更集中 |
| boost/function_score | 业务调权 | 文本相关性之外叠加标题、热度、时间等信号 |
文档 A 标题 8 个词,命中 “BM25” 1 次
文档 B 正文 8000 个词,命中 “BM25” 1 次
在其他条件接近时,A 往往更像用户想找的结果。
记忆钩子:BM25 不是“数词频”,而是“词频有用、稀有词更贵、短字段命中更集中”。
一个数字例子:Java 出现在 10 万篇文档里的 6 万篇,StampedLock 只出现在 800 篇。用户搜 Java StampedLock 时,后者的 IDF 更高,对排序区分更强;如果某篇文章把 StampedLock 堆了 100 次,BM25 的词频饱和也会抑制它无限加分。
- 误区:
_score越高就绝对越好。_score只在同一次查询、相同索引和相近查询条件下有比较意义,不适合作为跨查询的绝对分数。 - 误区:BM25 只看词出现次数。 它还考虑 IDF、字段长度归一化,并对词频增长做饱和处理。
- 误区:业务排序只能靠
_score。 生产里常用字段 boost、function_score、时间衰减、热度、销量等信号共同排序。 - 追问:为什么罕见词权重大? 罕见词能更有效区分文档,几乎每篇都有的词对判断相关性帮助很小。
- 追问:评分异常先看什么? 先用
_analyze看分词,再用explain看评分细节,随后检查 mapping、boost、同义词和 query 写法。 - 追问:为什么不能盲目加大热门度权重? 热门度过强会让不够相关但点击高的内容压过真正相关内容,搜索体验会偏离用户查询意图。
八、加强记忆
记住“BM25 看三件事:词多不多、词稀不稀、字段长不长”。面试时先解释 _score 的意义,再讲 TF、IDF、字段长度归一化,最后补充业务调权和排查工具,这题就很完整。