搜索時傳
filter參數報invalid_search_filter: Attribute "X" is not filterable,根因是該屬性未在索引filterableAttributes設置中聲明。Meilisearch 要求先在 settings 裡聲明可過濾屬性,才能在搜索時用作 filter。解法是先PUT /indexes/{uid}/settings/filterable-attributes聲明屬性,等任務完成後再搜索。適用 Meilisearch 全版本。
Issue & Env(案發現場與環境)
- 業務場景:文檔已索引成功,搜索不帶 filter 能正常返回結果。加上
filter參數後,返回 400 Bad Request,錯誤碼invalid_search_filter。 - 環境:Meilisearch v1.54.3,curl / 任意 SDK。
- 關鍵區分:這不是 filter 表達式語法錯誤,也不是字段名拼寫錯誤,是該屬性根本沒被聲明為可過濾。
StackTrace(堆疊日誌)
屬性未聲明為 filterable 時,搜索返回的完整錯誤響應(官方規範文檔原文):
{
"message": "Attribute `genres` is not filterable. Available filterable attributes are: ``.",
"code": "invalid_search_filter",
"type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid_search_filter"
}
如果索引已有部分 filterable 屬性但缺了正在用的那個:
{
"message": "Attribute `rating` is not filterable. Available filterable attributes are: `genres, language`.",
"code": "invalid_search_filter",
"type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid_search_filter"
}
filterableAttributes 為空時還有一種變體:
{
"message": "Attribute `genres` is not filterable. This index does not have configured filterable attributes.",
"code": "invalid_search_filter",
"type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid_search_filter"
}
以上均為 HTTP 400 Bad Request 同步返回(不是異步任務錯誤)。
Reproduce(復現程式碼)
# 1. 添加文档(假设已有索引和文档)
# 2. 直接带 filter 搜索——报错
curl -X POST 'http://localhost:7700/indexes/books/search' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer aSampleMasterKey' \
--data-binary '{
"q": "classic",
"filter": "genres = Classics"
}'
# 返回 400:
# {"message": "Attribute `genres` is not filterable. This index does not have configured filterable attributes.", ...}
Root Cause(根因分析)
filterableAttributes 是前置聲明機制
Meilisearch 的 filter 不是在搜索時遍歷文檔字段實現的。官方文檔原文:
Before you can filter, sort, or facet on an attribute, you must declare it in your index settings. Add attributes to
filterableAttributesto enable filtering and faceting.
Meilisearch 在設置 filterableAttributes 後,會為這些屬性構建優化的內部數據結構(如布隆過濾器、倒排索引的補充結構),搜索時直接查這些結構,不需要遍歷所有文檔。這是性能設計——如果每次搜索都遍歷全部字段做過濾,大數據集下性能會崩潰。
聲明是異步的
PUT /indexes/{uid}/settings/filterable-attributes 也是一個異步操作,返回 taskUid。必須等任務變為 succeeded 後,filter 才生效。如果在任務還在 enqueued 時就搜索,仍然報 not filterable。
facets 也依賴 filterableAttributes
facets 參數(返回分面統計)同樣要求屬性在 filterableAttributes 中聲明。但與 filter 不同的是,如果 facets 裡的屬性未聲明,Meilisearch 靜默忽略它(不報錯,只是不返回該分面的統計),而不是拋 400。這是個容易混淆的點。
The Fix(解決方案)
第一步:聲明 filterableAttributes
curl -X PUT 'http://localhost:7700/indexes/books/settings/filterable-attributes' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer aSampleMasterKey' \
--data-binary '["genres", "rating", "language"]'
# 返回 taskUid
也可以用 PATCH /indexes/{uid}/settings 一次更新多個設置項:
curl -X PATCH 'http://localhost:7700/indexes/books/settings' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer aSampleMasterKey' \
--data-binary '{
"filterableAttributes": ["genres", "rating", "language"],
"sortableAttributes": ["rating"]
}'
第二步:等待設置任務完成
# 用返回的 taskUid 轮询
curl http://localhost:7700/tasks/1 \
-H 'Authorization: Bearer aSampleMasterKey'
# 等待 status 变为 succeeded
第三步:帶 filter 搜索
curl -X POST 'http://localhost:7700/indexes/books/search' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer aSampleMasterKey' \
--data-binary '{
"q": "classic",
"filter": "genres = Classics",
"facets": ["genres", "rating", "language"]
}'
現在 filter 正常工作,facetDistribution 也返回 genres、rating、language 的統計。
注意事項
- 設置更新是異步的,必須等
status: succeeded後再搜索,否則仍然報not filterable。 - 添加
filterableAttributes會觸發索引重建(構建過濾數據結構),大數據集需要一定時間。 - 官方建議在添加文檔之前就設好 settings,避免雙重建索引。
sortableAttributes是獨立設置,與filterableAttributes不共享。排序屬性要單獨聲明。- filter 表達式語法:字符串用
=,數值用>,<,>=,<=。AND、OR、NOT組合,IN [a, b]匹配多值。
References(參考來源)
- Error Format and Definitions — Meilisearch 規範文檔meilisearch-spec
- Error codes — Meilisearch 官方文檔meilisearch-docs
- Filtering, sorting, and faceting — Meilisearch 官方文檔meilisearch-docs
- Build faceted navigation — Meilisearch 官方文檔meilisearch-docs
- Search API — Meilisearch 規範文檔meilisearch-spec
