1. 什么是 Search Template?能解決什么問題?
搜索模板是存儲在 ES 集群里的 Mustache 模板(lang: mustache
)。你把一份標準 _search
請求體寫成模板,變量交給 params,每次調用只需傳參即可:
- 搜索前端:把輸入框內容作為
params
傳進來,屏蔽 DSL 細節與危險語法。 - 業務后端:把“檢索策略”與“業務代碼”解耦——需要改查詢邏輯時,只改模板,不用發版。
- 多團隊協作:數據/搜索工程師直接維護模板,應用側只負責傳參和渲染結果。
模板以腳本的形式存在集群狀態中(Stored Script),受腳本開關與限制影響(如禁用腳本、限制大小等)。
2. 快速上手(5 分鐘)
2.1 創建/更新模板
PUT _scripts/my-search-template
{"script": {"lang": "mustache","source": {"query": { "match": { "message": "{{query_string}}" } },"from": "{{from}}","size": "{{size}}"}}
}
2.2 本地渲染與調試(不真正搜索)
POST _render/template
{"id": "my-search-template","params": { "query_string": "hello world", "from": 20, "size": 10 }
}
2.3 運行模板化搜索
GET my-index/_search/template
{"id": "my-search-template","params": { "query_string": "hello world", "from": 0, "size": 10 }
}
2.4 批量運行(多查詢一起發)
GET my-index/_msearch/template
{ }
{ "id": "my-search-template", "params": { "query_string": "hello world", "from": 0, "size": 10 } }
{ }
{ "id": "my-other-search-template", "params": { "query_type": "match_all" } }
2.5 管理模板
- 獲取某個模板:
GET _scripts/my-search-template
- 列出全部:
GET _cluster/state/metadata?filter_path=metadata.stored_scripts
- 刪除:
DELETE _scripts/my-search-template
3. Mustache 語法要點(結合搜索的“剛需功能”)
Mustache 是邏輯少的模板語言,靠變量替換 + 區塊控制完成拼裝。
3.1 變量與默認值
POST _render/template
{"source": {"query": { "match": { "message": "{{query_string}}" } },"from": "{{from}}{{^from}}0{{/from}}","size": "{{size}}{{^size}}10{{/size}}"},"params": { "query_string": "hello world" }
}
{{var}}
:變量{{^var}}default{{/var}}
:當var
不存在/為空時用默認值
3.2 條件與 if-else
"filter": [{{#year_scope}}{ "range": { "@timestamp": { "gte": "now-1y/d", "lt": "now/d" } }},{{/year_scope}}{ "term": { "user.id": "{{user_id}}" } }
]
{{#cond}}...{{/cond}}
:cond 為真則渲染{{^cond}}...{{/cond}}
:cond 為假則渲染- 兩者可組合做 if-else
3.3 工具函數(Lambda)
- URL 編碼:
{{#url}}{{host}}/{{page}}{{/url}}
- 拼接數組:
{{#join delimiter='||'}}date.formats{{/join}}
- 轉 JSON:
{{#toJson}}tags{{/toJson}}
(數組/對象都能轉)
?? 強烈建議:凡是數組、對象、子查詢塊,一律用
toJson
輸出,避免引號/逗號導致的無效 JSON。
3.4 列表循環與“尾逗號”陷阱
"fields": [{{#text_fields}}{{user_name}}{{^last}},{{/last}}{{/text_fields}}]
- 用一個布爾字段
last
控制是否加逗號,避免[a,b,]
這種無效 JSON。
3.5 更換變量分隔符(小眾)
{{=( )=}}
"message": "(query_string)"
(={{ }}=)
在模板內部臨時把 {{ }}
改為 ()
,適用于與其他模板語言沖突的場景。
不支持:Mustache 的 partials 特性在 ES 搜索模板里不可用。
4. 進階:把“復雜檢索策略”模板化
Search Template 的 source
支持 _search
的全部請求體,你可以模板化任何結構:Query DSL、Retrievers、kNN、RRF、LTR 重排、Async Search 參數等。
4.1 混合檢索(RRF)模板
PUT _scripts/rrf-template
{"script": {"lang": "mustache","source": {"retriever": {"rrf": {"rank_window_size": "{{rank_window}}{{^rank_window}}100{{/rank_window}}","retrievers": [{ "standard": { "query": { "match": { "text": "{{q}}" } } } },{ "standard": { "query": { "sparse_vector": {"field": "vector.tokens", "inference_id": "{{elser_id}}", "query": "{{q}}"}}}}]}},"size": "{{size}}{{^size}}10{{/size}}"}}
}
使用:
GET my-index/_search/template
{"id": "rrf-template","params": { "q": "blue shoes sale", "elser_id": "my-elser-endpoint", "size": 20 }
}
4.2 LTR(學習排序)重排模板
PUT _scripts/ltr-rescore
{"script": {"lang": "mustache","source": {"retriever": {"rescorer": {"retriever": { "standard": { "query": { "multi_match": {"query": "{{q}}", "fields": ["title^2","content"]}}}},"window_size": "{{win}}{{^win}}100{{/win}}","learning_to_rank": {"model_id": "{{model_id}}","params": { "query_text": "{{q}}" }}}},"from": "{{from}}{{^from}}0{{/from}}","size": "{{size}}{{^size}}10{{/size}}"}}
}
生產要點:
window_size
≥from + size
,否則可能出現未重排的文檔排在前面。
4.3 可選項與權限控制
- 把“是否限定時間范圍”“是否加高權重字段”等開關做成布爾參數,套
{{#cond}}...{{/cond}}
。 - 把“敏感過濾”寫入模板,前端只能傳白名單參數,降低注入風險。
5. 工程化落地清單
- 命名與版本
- 模板 id 采用
search.<domain>.<scenario>.v1
,修改不兼容時遞增版本,便于回滾。
- 參數白名單
- 應用層只接受你定義好的
params
字段;對字符串做長度限制、對數組做最大項數限制。
- 渲染前自檢
- 先
_render/template
;若 JSON 解析失敗立即中斷,返回明確錯誤給上游。
- 監控與審計
- 記錄模板 id、參數快照(脫敏)、渲染耗時、命中率、took、超時率,為回歸與 A/B 提供數據。
- 性能開關
- 大范圍查詢配
timeout
;只要命中數就size:0
+terminate_after
; - 不追求精確總數就別
track_total_hits:true
(或給個閾值),保性能可控。
- 安全與腳本限制
- 了解腳本相關設置(如禁用腳本會影響模板);控制模板大小與數量,避免集群狀態膨脹。
6. 常見坑與排雷
- 尾逗號/引號錯位:數組/對象拼裝一律用
toJson
;列表循環用last
變量收尾。 - 空變量導致非法查詢:對可空字段加默認值或用條件包裹。
- 把用戶輸入直塞進 DSL:一定做白名單與長度校驗(模板只是替換,不會自動防注入)。
- 腳本被禁用:排查集群腳本設置(模板屬于 stored script)。
- 多團隊改動互相影響:對模板加 代碼評審/發布流程,并約定 id 與版本策略。
7. 一鍵遷移套路(給你一套模版骨架)
PUT _scripts/search.web.v1
{"script": {"lang": "mustache","source": {"query": {"bool": {"filter": [{{#time_range}}{ "range": { "@timestamp": { "gte": "{{gte}}", "lte": "{{lte}}" } } },{{/time_range}}{{#env}}{ "term": { "service.env": "{{env}}" } },{{/env}}{{#category}}{ "term": { "category.keyword": "{{category}}" } }{{/category}}],"must": [{{#q}}{ "simple_query_string": { "query": "{{q}}", "fields": ["title^2","content"] } }{{/q}}]}},"_source": ["@timestamp","title","url","snippet"],"from": "{{from}}{{^from}}0{{/from}}","size": "{{size}}{{^size}}10{{/size}}","timeout": "{{timeout}}{{^timeout}}2s{{/timeout}}"}}
}
8. 小結
- Search Template = “可配置的搜索策略”:把 DSL/檢索管線參數化,統一在 ES 側托管。
- Mustache 提供拼裝能力:默認值、條件、列表、URL 編碼、
toJson
一應俱全。 - 與新特性無縫結合:Retrievers / RRF / LTR / 語義重排 都能以模板方式下發。
- 工程實踐很關鍵:參數白名單、渲染自檢、監控審計、版本與回滾,要一步到位。
如果你貼上真實索引 mapping 和 期望的檢索策略(詞法/語義/融合/重排),我可以幫你把它們整理成一套模板庫(含渲染/校驗腳本與 Kibana 演示面板),直接接入生產。