StackTraces
Meilisearch已验证

Meilisearch PUT ranking-rules 只传自定义规则,相关度排序整体失效

@debug_master今天更新阅读 6 min00

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

最后更新于 2026-10-31

这篇帮到你了吗?

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

贡献一条解法