跟著這篇走完,你會在本地把 Meilisearch 的多索引聯查跑通:movies 索引已經在(複用第一篇的),再補建 movie_ratings 和 movie_people 兩個索引——這樣手上就有三個獨立索引了。然後你用同一個端點 /multi-search 演示兩種模式:不傳 federation 就是各歸各的 results 陣列(適合電商搜索欄的分區塊 UI),傳了 federation: {} 就是合併重排的單條 hits 列表(適合全局搜索框),再調 weight 讓某個索引排靠前、用 distinct 跨索引去重、換兩套分頁體系。整個過程 6 步,每步一條驗證命令。localhost:7700 + aSampleMasterKey 跟系列前幾篇一致,索引名固定用 movies / movie_ratings / movie_people——你換了端口、key 或索引名,把示例值替換掉就行。
這篇預設你已經跑通了從零跑通 Meilisearch 本地搜索服務——Meilisearch 啟動、異步任務輪詢、movies.json 下載導入的套路全部複用,不再重複。跟系列前 8 篇的區別說在前面:前 8 篇搜的都是單索引內部的數據(中文分詞、分面過濾、排序排名、向量語義……),這篇第一次跨出去——一次 HTTP 請求,搜多個索引,要麼各歸各展示,要麼合併重排後一起返回。這也正是 Meilisearch multi-search 官方文檔裡兩種模式(multi-index / federated)的分野。

前置條件
| 項 | 要求 | 說明 |
|---|---|---|
| Meilisearch | v1.54.3 或更高 | multi-search 和 federated search 都是穩定特性,低於這代版本 federation.distinct 等細節可能缺失 |
| movies.json | 已下載並導入 movies 索引 | 沒導回第一篇第三步;本文不重複下載導入 |
| master key | aSampleMasterKey |
與系列一致 |
| curl | 任意版本 | 全文 curl 調 REST API |
先跑這條確認服務活著且 movies 索引已在。
curl 'http://localhost:7700/indexes/movies/stats' \
-H 'Authorization: Bearer aSampleMasterKey'
{
"numberOfDocuments": 31944,
"isIndexing": false,
"fieldDistribution": { "id": 31944, "title": 31944, "genres": 31944, "release_date": 31944 }
}
返回 "isIndexing": false 且 numberOfDocuments 為 31944 左右就滿足條件。報 index_not_found 說明 movies 索引沒建,回第一篇補。
第一步:補建 movie_ratings 和 movie_people 兩個索引
movies 已經有了,但 multi-search 需要至少兩個不同索引才能展示跨索引效果。這一步補建 movie_ratings(評分數據)和 movie_people(演職人員),讓三個索引都能搜到 “batman” 相關的內容——movies 裡有 Batman 系列電影,movie_ratings 裡有這些電影的評分,movie_people 裡有出演這些電影的演員和導演。
先說明一個本文刻意做的設計:三個索引統一含 title 字段。因為後面第四步要用 federation.distinct 按 title 跨索引去重,而去重的硬前提是去重屬性名在各索引裡完全一致——如果 movies 叫 title、movie_ratings 叫 movie_title,distinct 用哪個名字都會漏掉另一個。這是跨索引搜索建模時最容易埋的坑,本文用統一的 title 把它避開,也順帶講清楚這條約束。
先顯式建兩個索引:
# movie_ratings 索引:字段 id / title / rating / votes
curl -X POST http://localhost:7700/indexes \
-H "Authorization: Bearer aSampleMasterKey" \
-H "Content-Type: application/json" \
-d '{"uid":"movie_ratings","primaryKey":"id"}'
# movie_people 索引:字段 id / title / name / role
curl -X POST http://localhost:7700/indexes \
-H "Authorization: Bearer aSampleMasterKey" \
-H "Content-Type: application/json" \
-d '{"uid":"movie_people","primaryKey":"id"}'
兩條命令都返回 taskUid 和 "status": "enqueued"。往兩個索引裡塞數據,都選 Batman 相關的,這樣後面搜 “batman” 時三個索引都能命中:
# movie_ratings:Batman 系列的评分数据(title 与 movies 里的电影名一致)
curl -X POST http://localhost:7700/indexes/movie_ratings/documents \
-H "Authorization: Bearer aSampleMasterKey" \
-H "Content-Type: application/json" \
--data-binary '[
{"id":1,"title":"Batman","rating":7.8,"votes":320000},
{"id":2,"title":"Batman Returns","rating":7.0,"votes":210000},
{"id":3,"title":"Batman Begins","rating":8.2,"votes":580000},
{"id":4,"title":"The Dark Knight","rating":9.0,"votes":1800000},
{"id":5,"title":"The Dark Knight Rises","rating":8.4,"votes":900000},
{"id":6,"title":"The Batman","rating":7.8,"votes":650000},
{"id":7,"title":"Batman v Superman","rating":6.4,"votes":730000}
]'
# movie_people:Batman 系列的演员和导演
curl -X POST http://localhost:7700/indexes/movie_people/documents \
-H "Authorization: Bearer aSampleMasterKey" \
-H "Content-Type: application/json" \
--data-binary '[
{"id":1,"title":"Batman","name":"Michael Keaton","role":"Actor"},
{"id":2,"title":"Batman","name":"Jack Nicholson","role":"Actor"},
{"id":3,"title":"Batman","name":"Tim Burton","role":"Director"},
{"id":4,"title":"Batman Begins","name":"Christian Bale","role":"Actor"},
{"id":5,"title":"The Dark Knight","name":"Heath Ledger","role":"Actor"},
{"id":6,"title":"The Dark Knight","name":"Christopher Nolan","role":"Director"},
{"id":7,"title":"The Batman","name":"Robert Pattinson","role":"Actor"},
{"id":8,"title":"The Batman","name":"Zoë Kravitz","role":"Actor"}
]'
說明一下:官方 movies.json 只有 title / genres / release_date / overview 四個字段,沒有評分和演職人員——那是另一個 IMDb 數據集才有的。所以這兩個索引是手動造的,故意選了 Batman 系列,讓三個索引搜同一個詞都能命中。
導入是異步任務,輪詢到 succeeded 後用 stats 確認:
curl 'http://localhost:7700/indexes/movie_ratings/stats' -H 'Authorization: Bearer aSampleMasterKey'
curl 'http://localhost:7700/indexes/movie_people/stats' -H 'Authorization: Bearer aSampleMasterKey'
返回體裡 numberOfDocuments 分別是 7 和 8 就說明兩個索引都導入完成。報 index_not_found 是索引沒建成功,先查建索引的任務狀態;isIndexing: true 是還在處理,再等等。
第二步:multi-index search——分開返回,各歸各
multi-search 端點是 POST /multi-search,請求體必須有 queries 陣列。不傳 federation 字段(或顯式傳 null)就是 multi-index 模式——Meilisearch 對每條 query 獨立執行搜索,返回一個 results 陣列,陣列裡每個元素對應一條 query 的結果集。官方文檔見 Multi-index search。
先跑一遍三個索引各搜 “batman”:
curl -X POST 'http://localhost:7700/multi-search' \
-H 'Authorization: Bearer aSampleMasterKey' \
-H 'Content-Type: application/json' \
--data-binary '{
"queries": [
{ "indexUid": "movies", "q": "batman", "limit": 3 },
{ "indexUid": "movie_ratings", "q": "batman", "limit": 3 },
{ "indexUid": "movie_people", "q": "batman", "limit": 3 }
]
}'
返回體是 {"results": [...]} 陣列,三條 query 各一個:
{
"results": [
{
"indexUid": "movies",
"hits": [
{ "id": 268, "title": "Batman", "genres": ["Fantasy", "Action"] },
{ "id": 364, "title": "Batman Returns", "genres": ["Action", "Fantasy"] },
{ "id": 1735, "title": "Batman & Robin", "genres": ["Action", "Science Fiction"] }
],
"query": "batman",
"processingTimeMs": 2,
"limit": 3,
"offset": 0,
"estimatedTotalHits": 8
},
{
"indexUid": "movie_ratings",
"hits": [
{ "id": 1, "title": "Batman", "rating": 7.8, "votes": 320000 },
{ "id": 3, "title": "Batman Begins", "rating": 8.2, "votes": 580000 }
],
"query": "batman",
"processingTimeMs": 0,
"limit": 3,
"offset": 0,
"estimatedTotalHits": 6
},
{
"indexUid": "movie_people",
"hits": [
{ "id": 1, "title": "Batman", "name": "Michael Keaton", "role": "Actor" },
{ "id": 3, "title": "Batman", "name": "Tim Burton", "role": "Director" }
],
"query": "batman",
"processingTimeMs": 0,
"limit": 3,
"offset": 0,
"estimatedTotalHits": 2
}
]
}
讀一下這個響應體的結構——這是 multi-index 模式的簽名:
- 頂層是
results陣列,長度等於 queries 陣列長度,順序也一一對應——results[0]是 movies 的結果、results[1]是 movie_ratings、results[2]是 movie_people。 - 每條 query 可以帶完全獨立的參數——上例裡三個 limit 都是 3,但你可以給 movies 寫
limit: 10、給 movie_ratings 寫limit: 2、給 movie_people 加個filter: "role = Director"——都獨立生效。 - 每條 results 的結構跟單個
POST /indexes/{uid}/search的響應完全一致——hits/estimatedTotalHits/processingTimeMs這些字段一個不差。意味著你現有的單索引搜索處理邏輯幾乎不用改,把輸入從單條換成陣列就行。
驗證命令:
curl -s -X POST 'http://localhost:7700/multi-search' \
-H 'Authorization: Bearer aSampleMasterKey' \
-H 'Content-Type: application/json' \
--data-binary '{"queries":[{"indexUid":"movies","q":"batman","limit":1},{"indexUid":"movie_people","q":"batman","limit":1}]}' \
| jq '.results | length'
返回 2——results 陣列長度等於 queries 長度就通了。返回 400 是某條 query 的 indexUid 拼錯了,multi-search 的 fail-fast 行為會讓整個請求直接失敗,哪怕其他 query 是合法的——這個坑見 FAQ 第 2 項。
第三步:federated search——合併重排,一條 hits 列表
剛才的 multi-index 模式把三個索引的結果分開返回——如果你的 UI 是搜索欄下面分三個區塊顯示(“電影:”、“評分:”、“相關人員:”),這正合適。但如果你想要的是一條統一的結果流——不管結果來自哪個索引,按相關度排在一條列表裡——那就要用 federated search。
切換條件很簡單:請求體里加一個 federation 字段,哪怕是空物件 {}。
curl -X POST 'http://localhost:7700/multi-search' \
-H 'Authorization: Bearer aSampleMasterKey' \
-H 'Content-Type: application/json' \
--data-binary '{
"federation": {},
"queries": [
{ "indexUid": "movies", "q": "batman" },
{ "indexUid": "movie_ratings", "q": "batman" },
{ "indexUid": "movie_people", "q": "batman" }
]
}'
返回體結構完全變了——從 {"results": [...]} 陣列變成了 {"hits": [...]} 單物件:
{
"hits": [
{
"id": 268, "title": "Batman", "genres": ["Fantasy", "Action"],
"_federation": { "indexUid": "movies", "queriesPosition": 0 }
},
{
"id": 1, "title": "Batman", "name": "Michael Keaton", "role": "Actor",
"_federation": { "indexUid": "movie_people", "queriesPosition": 2 }
},
{
"id": 1, "title": "Batman", "rating": 7.8, "votes": 320000,
"_federation": { "indexUid": "movie_ratings", "queriesPosition": 1 }
},
{
"id": 364, "title": "Batman Returns", "genres": ["Action", "Fantasy"],
"_federation": { "indexUid": "movies", "queriesPosition": 0 }
}
],
"processingTimeMs": 3,
"limit": 20,
"offset": 0,
"estimatedTotalHits": 16
}
這就是 federated search 的“啊哈時刻”——請求體加一行 "federation": {},返回體就從陣列變成物件,結果從各歸各的三段變成一條合併流,按 merge 算法重排,每條 hit 多出一個 _federation 字段標識它來自哪個索引。這個字段的有無,就是兩種模式的唯一開關。
讀一下 _federation 裡的字段:
| 字段 | 含義 |
|---|---|
indexUid |
這條文檔來自哪個索引 |
queriesPosition |
它在 queries 陣列裡的下標(0-based),同一個 indexUid 在 queries 裡出現兩次時能靠它區分 |
weightedRankingScore |
可選,配了 federationOptions.weight 時出現,值是 _rankingScore × weight |
Meilisearch 內部怎麼合併的?核心邏輯是歸一化 ranking score 後按字典序比較:先比 relevancy score(words / typo / attribute / exactness / vector 合併值),tie 再看 sort/geosort,再 tie 看全局 ranking score,完美 tie 按 queries 陣列順序優先。詳細規則見 federated search 官方文檔——本文不展開,你只需要知道「結果按多級 tiebreak 排成一條降序流」就夠用了。
驗證命令:
curl -s -X POST 'http://localhost:7700/multi-search' \
-H 'Authorization: Bearer aSampleMasterKey' \
-H 'Content-Type: application/json' \
--data-binary '{"federation":{},"queries":[{"indexUid":"movies","q":"batman"}]}' \
| jq 'has("hits") and (has("results") | not)'
返回 true——有 hits 沒有 results,就是 federated 模式。
第四步:weight 權重 + distinct 跨索引去重
federation 預設所有 query 等權合併——movies 的結果、movie_ratings 的結果、movie_people 的結果在排名上“公平競爭”,誰更相關誰排前面。但你大概率想要電影本身排在前面(用戶搜 “batman” 最可能是找電影),評分和演職人員作為補充。這就用到 weight。
weight:讓某個索引的結果排靠前
weight 是 federationOptions 裡的一個浮點乘數,預設 1.0:
weight > 1.0:該 query 的所有結果 ranking score 被乘大,更容易排在前面weight < 1.0:結果 ranking score 被乘小,靠後weight = 0.0:等效於排除該 query 的全部結果(負數非法)
讓 movies 權重拉到 1.5,另外兩個壓在 1.0 以下:
curl -X POST 'http://localhost:7700/multi-search' \
-H 'Authorization: Bearer aSampleMasterKey' \
-H 'Content-Type: application/json' \
--data-binary '{
"federation": {},
"queries": [
{ "indexUid": "movies", "q": "batman",
"federationOptions": { "weight": 1.5 } },
{ "indexUid": "movie_ratings", "q": "batman",
"federationOptions": { "weight": 0.6 } },
{ "indexUid": "movie_people", "q": "batman",
"federationOptions": { "weight": 0.6 } }
]
}'
返回體裡 movies 的結果會集中排在前面。想看每條 hit 的加權後分數,在 query 里加 "showRankingScore": true,這時 _federation.weightedRankingScore 字段會出現。
weight 調優建議:先用小步長試(1.2 / 0.8),不要一上來拉 2.0 vs 0.1。因為 weight 是乘法——如果 movies 裡有一條 highly relevant 的 batman 結果(原始 rankingScore 0.95),乘 1.5 後 1.425,確實穩在前;但 movie_people 裡一條特別精確的命中(直接精確匹配 title 字段),哪怕 weight 只有 0.6,乘出來的分也可能超過 movies 裡一條弱相關的。weight 是「偏置」不是「開關」。
distinct:跨索引去重
注意第三步的返回體——同一個 “Batman” 出現了三次:movies(電影)裡一條、movie_ratings(評分)裡一條、movie_people(演職員)裡兩條。federated 合併後這些會散落在列表裡。如果你想讓每個電影只佔一個位置,用 federation.distinct。
federation.distinct 做的是跨所有參與索引的去重,按指定屬性值去——三個索引都有 title,所以 "distinct": "title" 會讓 title 值相同的文檔只留一條。前提是:去重屬性必須在每個參與索引的 filterableAttributes 裡聲明過。
先給三個索引都聲明 title 可過濾:
curl -X PATCH 'http://localhost:7700/indexes/movies/settings/filterable-attributes' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer aSampleMasterKey' \
-d '["title","genres"]'
curl -X PATCH 'http://localhost:7700/indexes/movie_ratings/settings/filterable-attributes' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer aSampleMasterKey' \
-d '["title","rating"]'
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,再發帶 federation.distinct 的請求:
curl -X POST 'http://localhost:7700/multi-search' \
-H 'Authorization: Bearer aSampleMasterKey' \
-H 'Content-Type: application/json' \
--data-binary '{
"federation": { "distinct": "title" },
"queries": [
{ "indexUid": "movies", "q": "batman" },
{ "indexUid": "movie_ratings", "q": "batman" },
{ "indexUid": "movie_people", "q": "batman" }
]
}'
現在 “Batman” 這個 title 值只留一條,而不是 movies / movie_ratings / movie_people 各佔一條。這就是跨索引去重的效果。
兩個不能忘的邊界:第一,去重屬性必須在每個參與索引都聲明 filterable,任一索引漏了整體 400——見 FAQ 第 1 項;第二,同一個請求裡同時寫 "federation": {"distinct": "title"} 和某條 query 裡寫 "distinct": "title",會直接 400(invalid_multi_search_distinct),兩個 distinct 不兼容。
第五步:分頁的兩套體系
multi-search 的分頁有兩個變數:在哪層分頁(query 層還是 federation 層)和用哪種模式(offset/limit estimation 還是 page/hitsPerPage exhaustive)。
先講一個硬性限制:federated 模式下,queries 裡的 offset/limit/page/hitsPerPage 全部被忽略——引擎先跑完所有 query,合併完結果,再在 federation 層分頁。queries 裡寫了 limit 不報錯但不生效,這是個靜默坑,見 FAQ 第 3 項。
estimation 分頁(預設)
不傳任何分頁參數時 federation 預設 offset=0 / limit=20。顯式寫:
curl -X POST 'http://localhost:7700/multi-search' \
-H 'Authorization: Bearer aSampleMasterKey' \
-H 'Content-Type: application/json' \
--data-binary '{
"federation": { "offset": 10, "limit": 5 },
"queries": [
{ "indexUid": "movies", "q": "batman" },
{ "indexUid": "movie_ratings", "q": "batman" }
]
}'
返回體裡 offset: 10, limit: 5,跳過前 10 條返回接下來 5 條。這個模式性能好但 estimatedTotalHits 是估算值,做頁碼導航不夠精確。
exhaustive 分頁(精確總數)
需要精確知道總命中數(做“共 N 條結果、第 x/y 頁”這種 UI)時,用 page + hitsPerPage:
curl -X POST 'http://localhost:7700/multi-search' \
-H 'Authorization: Bearer aSampleMasterKey' \
-H 'Content-Type: application/json' \
--data-binary '{
"federation": { "page": 2, "hitsPerPage": 5 },
"queries": [
{ "indexUid": "movies", "q": "batman" },
{ "indexUid": "movie_ratings", "q": "batman" }
]
}'
返回體字段變了:
{
"hits": [ "..." ],
"processingTimeMs": 4,
"page": 2,
"hitsPerPage": 5,
"totalHits": 14,
"totalPages": 3
}
從 estimatedTotalHits 變成了 totalHits(精確值),多了 totalPages(總頁數)。代價是引擎要遍歷所有命中文檔來精確計數,大數據集下比 estimation 慢。
兩種分頁模式互斥——同一個 federation 物件裡同時寫 offset 和 page,行為未定義,實踐中避免。
multi-index 模式下的分頁
不傳 federation 時,每條 query 的 offset/limit/page/hitsPerPage 獨立生效——這跟 federated 模式相反。給 movies 寫 "limit": 5、給 movie_ratings 寫 "limit": 10,返回體裡 results[0].limit = 5、results[1].limit = 10,各自截斷各自的結果。
第六步:每條 query 帶獨立參數 + facetsByIndex
multi-search 每條 query 本質就是一個完整的 search 請求——所有 POST /indexes/{uid}/search 的參數都能用:filter / sort / attributesToRetrieve / facets / hybrid / vector 等。你可以在同一個請求裡,讓 movies 只搜 Action 片、讓 movie_ratings 只返回評分 ≥ 8.0 的記錄。
上一步已經給三個索引聲明瞭 filterableAttributes(movies 有 title+genres,movie_ratings 有 title+rating,movie_people 有 title),現在髮帶獨立 filter 的 federated search:
curl -X POST 'http://localhost:7700/multi-search' \
-H 'Authorization: Bearer aSampleMasterKey' \
-H 'Content-Type: application/json' \
--data-binary '{
"federation": {},
"queries": [
{ "indexUid": "movies", "q": "batman",
"filter": "genres = Action",
"attributesToRetrieve": ["id","title","genres"] },
{ "indexUid": "movie_ratings", "q": "batman",
"filter": "rating >= 8.0",
"attributesToRetrieve": ["id","title","rating"] },
{ "indexUid": "movie_people", "q": "batman",
"attributesToRetrieve": ["id","title","name","role"] }
]
}'
返回體裡 movies 的 hits 都帶 genres: ["Action", ...],movie_ratings 的 hits 都滿足 rating >= 8.0。每條 query 的 filter 互相獨立、互不干擾——movies 用 genres 過濾、movie_ratings 用 rating 過濾,這正是 multi-search 比“分別發三個 HTTP 請求”更方便的地方:網絡只走一次,但每條 query 保留獨立參數。
你可能注意到中文字段名的問題——這些數據是英文的,filter 用英文;如果你的數據是中文,filter 表達式裡的屬性名和值要用你自己的字段,跟這篇中文搜索對對口徑。
facetsByIndex:按索引分別返回分面分布
federation 物件還可以帶 facetsByIndex——指定哪些索引的哪些屬性要返回 facet 分佈:
curl -X POST 'http://localhost:7700/multi-search' \
-H 'Authorization: Bearer aSampleMasterKey' \
-H 'Content-Type: application/json' \
--data-binary '{
"federation": {
"facetsByIndex": {
"movies": ["genres"],
"movie_ratings": ["rating"]
}
},
"queries": [
{ "indexUid": "movies", "q": "batman" },
{ "indexUid": "movie_ratings", "q": "batman" }
]
}'
返回體多了一個 facetsByIndex 字段:
{
"hits": [ "..." ],
"facetsByIndex": {
"movies": {
"distribution": {
"genres": { "Action": 8, "Fantasy": 2, "Science Fiction": 1 }
}
},
"movie_ratings": {
"stats": {
"rating": { "min": 6.4, "max": 9.0 }
}
}
}
}
facetsByIndex 讓你能一次請求同時拿到 movies 的 genres 分佈和 movie_ratings 的 rating 數值範圍,不用分別在兩條 query 裡寫 facets 參數再在應用層拼回來。
核心概念
這張架構圖就是整條調用鏈路,從左到右看兩種模式怎麼分叉:

| 概念 | 是什麼 | 類比 |
|---|---|---|
| multi-search | 一個端點一次請求搜多個索引的能力,入口是 POST /multi-search |
像搜索引擎的“同時搜網頁+圖片+視頻”按鈕 |
| federation | multi-search 請求體裡的一個字段——有則合併重排返回單列表,無則各歸各返回陣列 | 像一個“合併開關”:關掉是多路返回,打開是一條統一結果流 |
| federation.weight | federationOptions 裡的浮點乘數,乘到 ranking score 上控制某 query 結果的優先級 |
像 DJ 臺的音量旋鈕:權重 1.5 的 query 聲音大一點,更容易被聽到 |
| federation.distinct | 跨所有參與索引按指定屬性值去重 | 像合併多個 Excel 表後按電影名去重——不管數據在哪個表,同名的只留一條 |
| fail-fast | multi-search 的錯誤處理策略:任一 query 參數非法,整個請求立即返回 400 | 像一條流水線:任一工位報錯,整條線停下,不會讓好的工位繼續出活 |
要點回顧
- 一個端點,兩種模式——
POST /multi-search的返回形態由federation字段決定:不傳返回results[](各歸各,適合分區塊 UI),傳了返回hits(合併單列,適合全局搜索框)。 - weight 是偏置不是開關——
federationOptions.weight是乘在 ranking score 上的浮點乘數(預設 1.0);用 1.2/0.8 這樣的小步長調,別一上來拉 2.0 vs 0.1。 - distinct 跨索引去重——
federation.distinct按屬性值跨所有參與索引去重,硬前提是去重屬性在每個參與索引都聲明過 filterable,任一漏了整體 400。 - 分頁分兩層兩套——federated 模式下
queries裡的 offset/limit 被靜默忽略,分頁要寫在federation物件裡:offset/limit(估算)或page/hitsPerPage(精確)二選一;multi-index 模式下各 query 獨立分頁。 - fail-fast 是原子語義——任一條 query 參數非法,整個 multi-search 立即 400,不返回部分成功;需要部分成功就在應用層併發發獨立 search。
常見問題
federation.distinct 報 Attribute “xxx” is not filterable
現象:髮帶 "federation": {"distinct": "title"} 的 multi-search 返回 400,消息說 distinct 的屬性名不是 filterable。
原因:federation.distinct 做的是跨索引去重,本質是對每個參與索引執行 filter 操作。去重屬性必須在每個參與索引的 filterableAttributes 設置裡顯式聲明過,任一索引漏了就整體失敗。這不是 bug 是設計——去重依賴 filter 能力。
相關文章:Meilisearch federation.distinct 要求每個索引聲明 filterableAttributes
multi-search 一條 query 報錯,整個請求全掛
現象:multi-search 裡三條 query,第二條的 indexUid 拼錯了(比如 movie_rating 少了 s),整個請求返回 400——第一條和第三條的結果也拿不到,雖然它們完全合法。
原因:multi-search 是原子請求——引擎先做一輪全請求參數校驗,任何一條 query 不通過就直接返回第一個錯誤,不進入執行階段。這就是 fail-fast 策略。如果你需要部分成功的語義(某條 query 掛了其他照樣返回),別用 multi-search,在應用層併發發獨立的 search 請求。
federated 模式下 queries 裡寫 offset/limit 不生效
現象:給 federated search 的某條 query 寫了 "limit": 5,但返回體裡還是預設 20 條,limit 參數被靜默忽略了——不報錯也不生效。
原因:federated search 的分頁是合併結果層面的——引擎先跑完所有 query(不做單 query 分頁),合併重排後再分頁。queries 裡的 offset/limit/page/hitsPerPage 在 federation 模式下被完全忽略。分頁參數必須寫在 federation 物件裡:federation.offset/limit(estimation)或 federation.page/hitsPerPage(exhaustive),二選一。
相關文章:Meilisearch federated search 在 queries 裡寫 offset/limit 被忽略
