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

這篇幫到你了嗎?

剛解決了一個棘手的報錯?花兩分鐘記錄下來,幫助下一個遇到同樣問題的開發者。

貢獻一條解法