PUT /settings/ranking-rules是全量覆蓋語義,不是增量追加。只傳一條自定義規則(比如["rating:desc"])會把預設 7 條全部沖掉——typo、proximity、attributeRank、wordPosition、exactness 的裁決全部退場,只有words靠隱式優先兜底。不報錯,但相關度排序整體失效,結果順序變成「命中詞數相同就按你的字段排」。
Issue & Env(案發現場與環境)
- 業務場景:想讓「評分高的書」常駐搜索前排,把
rating:desc寫進排名規則。為了「省事」只傳了這一個規則。結果發現:拼錯詞的文檔和拼寫正確的文檔平起平坐、命中標題的文檔不再比命中摘要的靠前,整個相關度排序像被拔掉了。 - 環境:Meilisearch v1.54+(預設排名規則為 7 條:
words、typo、proximity、attributeRank、sort、wordPosition、exactness,v1.36 起attribute拆分為attributeRank+wordPosition)。 - 示例索引用
books_rank(6 條書籍文檔,含title_en、rating字段)。其中兩條關鍵數據:
{"id": 2, "title": "黑暗森林", "title_en": "The Dark Forest", "rating": 9.2}
{"id": 6, "title": "影之森", "title_en": "The Forest of Shadows", "rating": 8.5}
StackTrace(無報錯,靜默失效)
第一步先看設置被覆蓋成什麼樣:
curl http://localhost:7700/indexes/books_rank/settings/ranking-rules \
-H "Authorization: Bearer aSampleMasterKey"
["rating:desc"]
返回體裡只剩一條規則——不是追加,是整個陣列被替換了。沒有任何報錯,PUT 成功、任務 succeeded。
第二步看搜索行為變化。用 q=forest 搜(兩個書名裡都有 forest,拼錯的場景同理):
curl -X POST http://localhost:7700/indexes/books_rank/search \
-H "Authorization: Bearer aSampleMasterKey" \
-H "Content-Type: application/json" \
-d '{"q":"forest"}'
預設 7 條規則下,attributeRank 和 wordPosition 會裁決:兩本書都命中 title_en,但《影之森》的 forest 在標題第 2 個詞位、《黑暗森林》在第 3 個詞位,wordPosition 讓《影之森》排前。覆蓋後這個裁決消失了:
默认规则:影之森 8.5 → 黑暗森林 9.2(wordPosition:词位靠前者胜)
覆盖后: 黑暗森林 9.2 → 影之森 8.5(只剩隐式 words 平局 → rating:desc 接管)
順序整個翻轉。相關度維度(詞位、字段權重、錯字數)全部不再參與排序。
Root Cause(根因分析)
PUT settings 全家都是全量覆蓋
Meilisearch 的所有 PUT /settings/* 子路由(包括 PUT /settings 整體)都是替換整個陣列/物件的語義。官方 settings API 文檔對 ranking-rules 更新端點的描述就是覆寫現有列表。想「追加」必須把舊規則原樣帶上。
沖掉預設規則後,裁決鏈只剩隱式 words
官方 relevancy 文檔寫明:即使你刪掉 words,Meilisearch 仍會隱式地按 words 優先排序——這是保底,不是完整相關度。被沖掉的其餘 5 條各管一攤:
| 被沖掉的規則 | 失效後果 |
|---|---|
typo |
拼錯詞的文檔與拼寫正確的文檔平起平坐(typo tolerance 仍放行匹配,只是不再按錯字數排序) |
proximity |
詞距不再影響順序,「dark forest」相鄰命中和相隔十詞命中一樣 |
attributeRank |
標題命中與摘要命中不再有先後 |
wordPosition |
詞位靠前不再加分 |
exactness |
精確整值匹配不再優先於部分匹配 |
words 的隱式優先意味著「命中詞數」還在起作用——所以現象是同命中數內部順序失控,而不是徹底亂序,這也是它容易瞞過測試的原因。
順帶澄清:自定義規則不需要 sortableAttributes
rating:desc 這類自定義排名規則只要字段在全部文檔裡是數字或字符串即可,不要求先聲明 sortableAttributes(那是 sort 搜索參數的要求)。所以坑不在聲明缺失,純在覆蓋語義。
The Fix(解決方案)
第一步:全量帶上預設 7 條,自定義規則放鏈尾
curl -X PUT http://localhost:7700/indexes/books_rank/settings/ranking-rules \
-H "Authorization: Bearer aSampleMasterKey" \
-H "Content-Type: application/json" \
--data-binary '["words","typo","proximity","attributeRank","sort","wordPosition","exactness","rating:desc"]'
放鏈尾是軟排序:相關度先裁決,打平時評分高的靠前。要「評分硬壓相關度」就放鏈首,代價是犧牲相關度——位置即權重。
第二步:拿不準就重置為預設
DELETE 該端點會把 rankingRules 恢復成預設 7 條(設置類端點的重置語義):
curl -X DELETE http://localhost:7700/indexes/books_rank/settings/ranking-rules \
-H "Authorization: Bearer aSampleMasterKey"
排查建議
- 改完規則先
GET /settings/ranking-rules看一眼,確認預設 7 條都在——全量覆蓋不留神就會少帶。 - 設置更新是異步任務且觸發全量重建,等任務
succeeded再驗證順序,否則看到的還是舊索引的結果。 - 用
_rankingScoreDetails驗證:預設規則下明細裡有typo、attributeRank等鍵;被覆蓋後這些鍵會消失,一眼就能看出裁決鏈缺了誰。 - v1.36+ 環境注意:
attribute不能與attributeRank/wordPosition混用,帶規則時別把新舊兩套寫在一起。
References(參考來源)
- Built-in ranking rules — Meilisearch 官方文檔meilisearch-docs
- Settings API reference — Meilisearch 官方文檔meilisearch-docs
