用
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
