multi-search 的错误处理策略是 fail-fast:请求里任意一条 query 参数非法,整个请求立即终止,返回一个错误对象——不返回任何部分成功的结果。这意味着你不能指望「好的 query 先返回,坏的 query 报错后重试」。必须确保所有 query 都合法后,multi-search 才会返回 results 数组或 federated hits 列表。
Issue & Env(案发现场与环境)
- 业务场景:电商全站搜索。
products和blog_posts两个索引都建好了,但写 multi-search 时手滑把blog_posts写成了blog_post(少了 s)。发出请求后返回 400,products的结果也没拿到——虽然它的 query 完全合法。 - 环境:Meilisearch v1.54+,任意 multi-search 请求。
- 关键区分:这不是网络超时或部分索引宕机——那会有不同的错误路径。fail-fast 是请求参数校验层面的行为:在 Meilisearch 引擎开始执行任何 query 之前,它先做一轮全请求的参数校验,校验通不过就直接返回第一个发现的错误。
StackTrace(错误响应)
{
"message": "Index `blog_post` not found.",
"code": "index_not_found",
"type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#index_not_found"
}
注:fail-fast 的错误响应就是第一个非法 query 对应的错误码——indexUid 不存在就报
index_not_found、filter 语法错就报invalid_search_filter、offset 为负数就报invalid_search_offset。错误消息只涉及第一个被发现的问题,不会列出全部。
Reproduce(复现代码)
# products 和 blog_posts 都已建好,blog_post 不存在
curl -X POST 'http://localhost:7700/multi-search' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer aSampleMasterKey' \
--data-binary '{
"queries": [
{ "indexUid": "products", "q": "headphones", "limit": 5 },
{ "indexUid": "blog_post", "q": "headphones" }
]
}'
{"message":"Index `blog_post` not found.","code":"index_not_found","type":"invalid_request","link":"https://docs.meilisearch.com/errors#index_not_found"}
products 那条 query 完全合法,但因为第二条 query 有不存在的 indexUid,整个请求返回 400,没有 results 数组,也没有任何 hits。
Root Cause(根因分析)
multi-search 是一个原子请求,不是并行子请求
Meilisearch 对 multi-search 的处理是:
- 解析整个请求体
- 对所有 queries 做一轮前置校验(indexUid 存在性、filter 语法、分页参数等)
- 校验通过后,并行执行所有 query,合并返回
校验阶段任何一条 query 不通过,第 3 步就不发生——这就是 fail-fast。引擎不会先跑第 1 条,发现第 2 条错了再返回第 1 条的结果。
对比:客户端自己并发
如果你不用 multi-search,而是在应用层同时发两个 POST /indexes/{uid}/search 请求,其中一个 indexUid 写错,另一个还是会正常返回——因为它们是独立的 HTTP 请求,独立的校验和执行。multi-search 把多个请求打包成一个,代价就是失去了部分成功的能力。
为什么这么设计
Meilisearch 的 multi-search 支持 federated mode(合并重排),这个合并步骤要求所有 query 的结果都拿到之后才能做。如果允许部分成功,合并逻辑就要处理「某些 query 超时/失败怎么办」——设计复杂度大幅上升。官方选择了 fail-fast,换来了简单的语义:要么全成功,要么全失败。
The Fix(解决方案)
第一步:每条 query 单独验证
multi-search 里的每条 query 先单独跑通再合并进来。
# 先单独验证 blog_posts(注意是复数)
curl -X POST 'http://localhost:7700/indexes/blog_posts/search' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer aSampleMasterKey' \
-d '{"q":"headphones"}'
{"hits":[...],"query":"headphones","processingTimeMs":1,"estimatedTotalHits":42}
单独跑通了再进 multi-search。
第二步:修正 multi-search 里的 indexUid
curl -X POST 'http://localhost:7700/multi-search' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer aSampleMasterKey' \
--data-binary '{
"queries": [
{ "indexUid": "products", "q": "headphones", "limit": 5 },
{ "indexUid": "blog_posts", "q": "headphones", "limit": 3 }
]
}'
{
"results": [
{ "indexUid": "products", "hits": [...], "processingTimeMs": 1 },
{ "indexUid": "blog_posts", "hits": [...], "processingTimeMs": 2 }
]
}
排查建议
- multi-search 的 indexUid 建议用配置常量或枚举,别硬编码字符串——打错了编译器/IDE 能发现。
- 错误消息只列第一个发现的问题——修完第一个重发,可能又冒出第二个。所以建议每条 query 先单独验证通过再合并。
- 如果你真的需要部分成功的语义(比如 3 个索引搜,其中一个暂时挂了但另外两个继续返回),别用 multi-search,在应用层并发发独立的 search 请求,哪个成功就用哪个的结果。
References(参考来源)
- Multi-search overview — Meilisearch 官方文档meilisearch-docs
- Multi-search API reference — Meilisearch 官方文档meilisearch-docs
