ES评分(score)是如何计算出来的
在 Elasticsearch(ES)中,explain 功能用于分析查询的评分(score)是如何计算出来的。它会详细展示每个文档的评分过程,包括哪些因素影响了评分以及每个因素的贡献。以下是对如何查看 explain 的评分、评分计算原理以及一个具体示例的详细说明。
1. 如何查看 explain 的评分
在 Elasticsearch 中,可以通过以下两种方式查看 explain 的评分信息:
- 使用
explainAPI:针对特定文档和查询,获取详细的评分计算过程。 - 在搜索请求中启用
explain参数:在搜索结果中为每个文档返回评分解释。
方法 1:使用 explain API
GET /<index>/_explain/<doc_id>
{
"query": {
"match": {
"field": "value"
}
}
}
<index>:索引名称。<doc_id>:文档 ID。- 返回结果会包含评分计算的详细分解。
方法 2:在搜索请求中启用 explain
在搜索查询中添加 "explain": true:
GET /<index>/_search
{
"explain": true,
"query": {
"match": {
"field": "value"
}
}
}
- 搜索结果中的每个
_hit会包含一个_explanation字段,描述评分的计算过程。
2. 评分是如何计算的
Elasticsearch 默认使用 BM25 相似度模型(以前版本使用 TF-IDF)来计算文档的评分。BM25 是一个基于词频、文档长度和查询词重要性的概率模型。评分计算主要基于以下因素:
关键因素
- 词频(Term Frequency, TF):
- 查询词在文档中出现的频率越高,评分越高。
- 但 BM25 对词频有饱和机制(即多次出现的效果会逐渐减弱)。
- 逆文档频率(Inverse Document Frequency, IDF):
- 查询词在索引中的稀有程度。词越稀有(出现在越少的文档中),IDF 越高,评分贡献越大。
- 字段长度归一化(Field Length Normalization):
- 文档字段的长度越短,评分越高(因为短字段中的词更重要)。
- 查询协调因子(Query Coordination Factor):
- 如果查询包含多个词,匹配更多查询词的文档会获得额外的评分加成。
- 其他因素:
- 如果使用
boost参数,某些字段或查询的权重会增加。 - 自定义评分函数(如
function_score)可能会修改评分。
- 如果使用
BM25 评分公式(简化版)
对于查询词 ( t ) 和文档 ( d ),BM25 的评分公式为:
[
\text{score}(d, q) = \sum_{t \in q} \text{IDF}(t) \cdot \frac{\text{TF}(t, d) \cdot (k_1 + 1)}{\text{TF}(t, d) + k_1 \cdot (1 - b + b \cdot \frac{|d|}{\text{avgdl}})}
]
- (\text{IDF}(t)):逆文档频率。
- (\text{TF}(t, d)):词 ( t ) 在文档 ( d ) 中的词频。
- (|d|):文档字段长度。
- (\text{avgdl}):索引中所有文档的平均字段长度。
- (k_1):控制词频饱和度的参数(默认 1.2)。
- (b):控制字段长度归一化的参数(默认 0.75)。
3. 示例
假设我们有一个索引 books,包含以下文档:
PUT /books/_doc/1
{
"title": "Elasticsearch Guide"
}
PUT /books/_doc/2
{
"title": "Elasticsearch in Action"
}
查询
我们执行以下查询,并启用 explain:
GET /books/_search
{
"explain": true,
"query": {
"match": {
"title": "Elasticsearch"
}
}
}
返回结果(简化)
假设返回的 _explanation 如下(实际输出会更详细):
{
"hits": {
"hits": [
{
"_id": "1",
"_score": 0.2876821,
"_source": { "title": "Elasticsearch Guide" },
"_explanation": {
"value": 0.2876821,
"description": "sum of:",
"details": [
{
"value": 0.2876821,
"description": "weight(title:elasticsearch in 1) [PerFieldSimilarity], result of:",
"details": [
{
"value": 0.2876821,
"description": "score(freq=1.0), computed as boost * idf * tfNorm",
"details": [
{ "value": 1.0, "description": "boost" },
{ "value": 0.6931472, "description": "idf, computed as log(1 + (N - n + 0.5) / (n + 0.5)) from:" },
{ "value": 0.4150375, "description": "tfNorm, computed as (freq * (k1 + 1)) / (freq + k1 * (1 - b + b * fieldLength / avgFieldLength))" }
]
}
]
}
]
}
},
{
"_id": "2",
"_score": 0.2876821,
"_source": { "title": "Elasticsearch in Action" },
"_explanation": {
"value": 0.2876821,
"description": "sum of:",
"details": [
{
"value": 0.2876821,
"description": "weight(title:elasticsearch in 2) [PerFieldSimilarity], result of:",
"details": [
{
"value": 0.2876821,
"description": "score(freq=1.0), computed as boost * idf * tfNorm",
"details": [
{ "value": 1.0, "description": "boost" },
{ "value": 0.6931472, "description": "idf, computed as log(1 + (N - n + 0.5) / (n + 0.5)) from:" },
{ "value": 0.4150375, "description": "tfNorm, computed as (freq * (k1 + 1)) / (freq + k1 * (1 - b + b * fieldLength / avgFieldLength))" }
]
}
]
}
]
}
}
]
}
}
解释
-
评分计算:
- 两个文档的评分都是
0.2876821,因为:- 词频(
freq=1.0):查询词"Elasticsearch"在两个文档的title字段中都只出现了一次。 - IDF(
0.6931472):因为索引中只有两个文档,且两个文档都包含"Elasticsearch",IDF 计算为log(1 + (2 - 2 + 0.5) / (2 + 0.5))。 - 字段长度归一化(
tfNorm=0.4150375):由于title字段长度不同(文档 1 的title为 2 个词,文档 2 为 3 个词),但 BM25 的归一化使得影响较小。
- 词频(
- 最终评分:
score = boost * idf * tfNorm = 1.0 * 0.6931472 * 0.4150375 ≈ 0.2876821。
- 两个文档的评分都是
-
为什么评分相同:
- 尽管文档 2 的
title字段更长,但 BM25 的长度归一化参数 ( b )(默认 0.75)降低了字段长度的影响。 - 词频和 IDF 相同,因此评分几乎一致。
- 尽管文档 2 的
4. 如何分析 explain 输出
- 关注
value:表示评分或子项的贡献值。 - 查看
description:描述评分计算的步骤(如idf、tfNorm等)。 - 检查
details:深入了解每个子项的计算过程。 - 异常排查:
- 如果评分意外高/低,检查是否使用了
boost或自定义评分。 - 确认字段长度是否影响了归一化。
- 检查查询词的 IDF 是否因索引中文档分布而异常。
- 如果评分意外高/低,检查是否使用了
5. 补充说明
- 自定义评分:如果需要调整评分逻辑,可以使用
function_score查询或修改字段映射中的boost参数。 - 调试技巧:在开发环境中,使用
explain检查评分是否符合预期,特别是在多字段查询或复杂查询中。 - 性能注意:启用
explain会增加查询开销,建议仅在调试时使用。

浙公网安备 33010602011771号