StackTraces
OpenSearch已验证

OpenSearch 按字段排序后 _score 消失:track_scores 默认 false

@debug_master今天更新阅读 6 min00

用 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(参考来源)

最后更新于 2026-10-31

这篇帮到你了吗?

刚解决了一个棘手的报错?花两分钟记录下来,帮助下一个遇到同样问题的开发者。

贡献一条解法