用
sort按year字段排序后,返回体hits里每条命中的_score字段不见了。这不是数据丢了,也不是排序报错——OpenSearch 对「按字段排序」的场景默认不计算相关性分数,track_scores参数默认是false。想让分数和排序一起返回,在请求体里加"track_scores": true即可。适用 OpenSearch 3.x。
Issue & Env(案发现场与环境)
- 业务场景:跟着 从零跑通 OpenSearch 本地搜索服务 建好
movies索引(5 条电影),用match搜wind rise时,返回体里每条 hit 都带一个_score。想按year从新到旧排,改成sort之后,_score突然全没了。 - 环境:OpenSearch 3.x(Docker latest 镜像),单节点,索引
movies(1 主分片、0 副本),security 插件开启(HTTPS + Basic Auth)。 - 关键区分:搜得到、也排序成功了,唯独
_score字段从每条 hit 里消失。不是查询写错,是一个默认开关在作怪。
StackTrace(现象:返回体里 _score 消失)
按字段排序后的返回体,每条 hit 只剩 _index / _id / _sort / _source,没有 _score:
{
"took": 3,
"timed_out": false,
"hits": {
"total": { "value": 3, "relation": "eq" },
"max_score": null,
"hits": [
{
"_index": "movies",
"_id": "3",
"_sort": [2021],
"_source": { "title": "The Rise of Machines", "year": 2021, "genre": "scifi" }
},
{
"_index": "movies",
"_id": "5",
"_sort": [2015],
"_source": { "title": "Windmill Valley", "year": 2015, "genre": "drama" }
}
]
}
}
注意顶层 max_score 也变成了 null,多条 hit 里都不再出现 _score。
Reproduce(复现代码)
前提:本地 OpenSearch 已启动,movies 索引已按 quickstart 建好并导入 5 条文档。
先看纯相关性的返回,_score 都在:
curl -sk -u admin:Devfix@2026 -X GET "https://localhost:9200/movies/_search?pretty" \
-H "Content-Type: application/json" \
-d '{ "query": { "match": { "title": "wind rise" } } }'
再改成按 year 降序,_score 消失:
curl -sk -u admin:Devfix@2026 -X GET "https://localhost:9200/movies/_search?pretty" \
-H "Content-Type: application/json" \
-d '{ "query": { "match": { "title": "wind rise" } }, "sort": [{ "year": "desc" }] }'
判定标准:第二个请求的 hits.hits[] 里没有任何 _score 字段、max_score 为 null,即复现成功。
Root Cause(根因分析)
按字段排序时,分数对“谁排前面”没有用了
_score 是 BM25 算出来的相关性分,作用只有一个:决定结果按相关性排序。一旦你显式指定 sort 按别的字段排序,排序依据就从「相关性分」换成了「那个字段的值」,_score 不再参与排序,OpenSearch 默认就不算了,省下这一步的开销。
官方 Sort results 文档的说法是:By default, scores are not computed when sorting on a field. 配套的开关是 track_scores,Search API 文档标注它默认 false——含义正是「排序时要不要计算并返回文档分数」。
为什么是「不算」而不是「算出来藏起来」
这不是把 _score 藏了。相关性分依赖分词词频、逆文档频率、字段长度归一化这些统计量,按字段排序时 OpenSearch 直接从 doc values 取字段值排,根本没进打分链路,所以既没有值、max_score 也置 null。想让它把分也算出来,得显式开 track_scores,代价是多付一笔打分开销。
The Fix(解决方案)
方案一:sort 时加 track_scores=true(要分数就开)
在请求体顶层加 "track_scores": true:
curl -sk -u admin:Devfix@2026 -X GET "https://localhost:9200/movies/_search?pretty" \
-H "Content-Type: application/json" \
-d '{ "query": { "match": { "title": "wind rise" } }, "sort": [{ "year": "desc" }], "track_scores": true }'
返回体里每条 hit 会重新带上 _score,max_score 也会填上值。排序顺序不变(还是按 year),只是分数跟着一起算出返回。
方案二:排序目标就是相关性,就别加无关 sort
如果你其实想要「相关性最高的排前面」,那根本不需要 sort——去掉 sort,OpenSearch 默认就按 _score 降序,_score 自然也都在。只有当排序依据必须是某个字段值时,才轮到 track_scores 这个开关出场。
注意事项
track_scores是顶层参数,别写进sort数组里的某个元素,那里只认字段名: 排序方向。- 开
track_scores有计算开销:每一篇命中都要重新走一遍打分,文档量大时能感到变慢。只在确实要展示分数时开。 - 分页翻页(
from/size)时同样适用:凡是有字段sort、又想让每条结果带分数,track_scores: true一起带上最省心。
References(参考来源)
- Sort results — OpenSearch 官方文档opensearch-docs
- Search API — OpenSearch 官方文档opensearch-docs
