StackTraces
Meilisearch已驗證

Meilisearch federation.distinct 要求每個索引聲明 filterableAttributes

@debug_master1 天前更新閱讀 6 min00

federated search 的 distinct 參數做的是跨索引去重,它要求去重屬性名在每個參與查詢的索引的 filterableAttributes 列表裡都聲明過。任一索引漏聲明,整個 multi-search 請求 400 失敗。這不是 bug 是設計——去重本質是跨索引的 filter 操作,而 filter 參數依賴 filterableAttributes 聲明。

Issue & Env(案發現場與環境)

  • 業務場景:三個索引 movies / movie_ratings / movie_people 都有 title 字段。movies 已經 PATCH 過 filterableAttributes: ["title"],另外兩個索引漏了。發 federated search 帶 federation.distinct: "title" 時返回 400。
  • 環境:Meilisearch v1.54+,任意 federated multi-search 請求。
  • 關鍵區分:這不是 invalid_multi_search_distinct(那個錯誤是 federation.distinct 和 query 級 distinct 同時存在),也不是普通的 invalid_search_filter(那個是單索引 filter 語句裡用了未聲明的屬性)。federation.distinct 觸發的是跨索引 filter 校驗,錯誤消息裡的屬性名是 federation 參數裡的值。

StackTrace(錯誤響應)

{
  "message": "Attribute \"title\" is not filterable.",
  "code": "invalid_search_filter",
  "type": "invalid_request",
  "link": "https://docs.meilisearch.com/errors#invalid_search_filter"
}

注:錯誤碼是 invalid_search_filter(和單索引 filter 未聲明的錯誤碼一樣),但觸發路徑是 federation.distinct 跨索引校驗。具體 message 可能隨 Meilisearch 版本略有差異,但「xxx is not filterable」這個結構是穩定的。

Reproduce(復現程式碼)

# 1. 三个索引都已建,movies 已声明 title 可过滤,movie_ratings 和 movie_people 没声明
curl -X PATCH 'http://localhost:7700/indexes/movies/settings/filterable-attributes' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer aSampleMasterKey' \
  -d '["title"]'

# 2. 直接发带 federation.distinct 的请求 —— 400 失败
curl -X POST 'http://localhost:7700/multi-search' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer aSampleMasterKey' \
  --data-binary '{
    "federation": { "distinct": "title" },
    "queries": [
      { "indexUid": "movies", "q": "batman" },
      { "indexUid": "movie_ratings", "q": "batman" },
      { "indexUid": "movie_people", "q": "batman" }
    ]
  }'
{"message":"Attribute \"title\" is not filterable.","code":"invalid_search_filter","type":"invalid_request","link":"https://docs.meilisearch.com/errors#invalid_search_filter"}

Root Cause(根因分析)

federation.distinct 是跨索引的 filter 操作

Meilisearch 的 federated search 合併多索引結果後,要按 federation.distinct 指定的屬性值去重。這個去重動作對每個參與索引來說,等價於在各自索引上做一次「只保留某個屬性值唯一的文檔」——而這個操作依賴該屬性在索引裡聲明為 filterable。

不是「每個索引都要有這個字段」,是「每個索引都要聲明它可過濾」

某個索引的文檔裡哪怕根本沒有 title 這個字段,只要你在 federation.distinct 裡寫了它,Meilisearch 就會去每個參與索引的 filterableAttributes 列表裡查——查不到就報錯。這是一個聲明層面的校驗,不檢查實際數據。

和 invalid_multi_search_distinct 的區別

Meilisearch 還有另一個 multi-search 相關錯誤碼 invalid_multi_search_distinct——它的觸發條件是 federation 級 distinct 和 query 級 distinct 同時存在。而本條 case 的觸發條件是某個索引沒在 filterableAttributes 裡聲明 distinct 屬性,錯誤碼是 invalid_search_filter。兩者別混淆。

The Fix(解決方案)

第一步:給所有參與索引補聲明

curl -X PATCH 'http://localhost:7700/indexes/movie_ratings/settings/filterable-attributes' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer aSampleMasterKey' \
  -d '["title"]'

curl -X PATCH 'http://localhost:7700/indexes/movie_people/settings/filterable-attributes' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer aSampleMasterKey' \
  -d '["title"]'

等兩個 PATCH 任務都 succeeded 後重發 multi-search 請求。

第二步:驗證 filterableAttributes 已生效

curl 'http://localhost:7700/indexes/movie_ratings/settings/filterable-attributes' \
  -H 'Authorization: Bearer aSampleMasterKey'
["title"]

排查建議

  • federation.distinct 的屬性名提前拿個 checklist:凡是參與 federated search 的索引,都要在 filterableAttributes 裡聲明它。別假設「這個索引沒這個字段所以不需要聲明」——Meilisearch 不做這個推斷。
  • 如果某個索引真的不應該參與去重(比如它的文檔結構里根本沒有 title),那它就不應該出現在 queries 陣列裡,或者換一個所有索引共有的屬性名做 distinct。
  • PATCH filterableAttributes 是異步操作,任務 succeeded 之前別重發 multi-search——重發會得到同樣的 400。

附帶提醒:federation.distinct 按屬性值去重還要求字段名跨索引一致。如果一個索引叫 title、另一個叫 movie_title,distinct 用哪個名字都會漏掉另一個——建模時就要統一去重字段的命名。

References(參考來源)

最後更新於 2026-10-29

這篇幫到你了嗎?

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

貢獻一條解法