搜索时传
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
