一步步拆解解決 Elasticsearch 檢索模板問題
1、線上實戰(zhàn)提問
Elasticsearch做模版查詢的時候,在使用 terms 進(jìn)行批量查詢的時候放入數(shù)組在模版中進(jìn)行查詢失敗,類似于模版?zhèn)魅霐?shù)組該如何實現(xiàn)?
問題來源:死磕Elasticsearch知識星球
#?定義索引
PUT?uint-2020-08-17
{
????"mappings":?{
??????"properties":?{
????????"clock":?{
??????????"type":?"date",
??????????"format":?"epoch_second"
????????},
????????"itemid":?{
??????????"type":?"long"
????????},
????????"ns":?{
??????????"type":?"long"
????????},
????????"ttl":?{
??????????"type":?"long"
????????},
????????"value":?{
??????????"type":?"long"
????????}
??????}
????}
}
#?添加內(nèi)容
PUT?uint-2020-08-17/_bulk
{?"index"?:?{??"_id"?:?"1"?}?}
{"itemid":1,"ns":643214179,"clock":1597752311,"value":"1123","ttl":604800}
{?"index"?:?{??"_id"?:?"2"?}?}
{"itemid":2,"ns":643214179,"clock":1597752311,"value":"123555","ttl":604800}
{?"index"?:?{??"_id"?:?"3"?}?}
{"itemid":3,"ns":643214179,"clock":1597752311,"value":"1","ttl":604800}
{?"index"?:?{??"_id"?:?"4"?}?}
{"itemid":4,"ns":643214179,"clock":1597752311,"value":"134","ttl":604800}
{?"index"?:?{??"_id"?:?"5"?}?}
{"itemid":2,"ns":643214179,"clock":1597752311,"value":"123556","ttl":604800}
查詢語句:
PUT?_scripts/item_agg
{
??"script":?{
????"lang":?"mustache",
????"source":?{
??????"_source":?[
????????"value"
??????],
??????"size":?0,
??????"query":?{
????????"bool":?{
??????????"filter":?[
????????????{
??????????????"terms":?"{{#toJson}}statuses{{/toJson}}"
????????????},
????????????{
??????????????"range":?{
????????????????"clock":?{
??????????????????"gte":?"{{startTime}}",
??????????????????"lte":?"{{endTime}}"
????????????????}
??????????????}
????????????}
??????????]
????????}
??????},
??????"aggs":?{
????????"group_terms":?{
??????????"terms":?{
????????????"field":?"itemid"
??????????},
??????????"aggs":?{
????????????"avg_value":?{
??????????????"avg":?{
????????????????"field":?"value"
??????????????}
????????????},
????????????"max_value":?{
??????????????"max":?{
????????????????"field":?"value"
??????????????}
????????????}
??????????}
????????}
??????}
????}
??}
}
查詢模版參數(shù):
POST?uint-*/_search/template
{
??"id":?"item_agg",
??"params":?{
????"itemid":{
??????"statuses":[1,2]
????},
????"startTime":1597752309,
????"endTime":1597752333
????
??}
}
以上內(nèi)容看著很長,根據(jù)注釋拆解為:
定義索引、 插入數(shù)據(jù)、 創(chuàng)建模板、 構(gòu)造參數(shù)檢索
四個子部分你就不會恐慌了。
2、知識點解讀——搜索模板
2.1 什么是搜索模板?
很多人都聽說使用過 索引模板 index template,索引模板的好處:
便于跨索引統(tǒng)一建模; 尤其適合數(shù)據(jù)量巨大、索引字段類似的業(yè)務(wù)系統(tǒng); 靈活便捷。
檢索模板(search template)大家使用相對較少,在實戰(zhàn)業(yè)務(wù)場景中:每次業(yè)務(wù)請求都要構(gòu)造 DSL,比如:這次查title、下次查content,除此之外的 DSL 部分 都一樣,但兩次請求:后端代碼那里就要有相應(yīng)的修改和適配。有沒有不修改、拼接DSL使用檢索的方案?這就引出了搜索模板。
搜索模板與關(guān)系數(shù)據(jù)庫中的存儲過程非常相似??梢詫⒊S貌樵兌x為模板,并且使用 Elasticsearch 的應(yīng)用程序可以簡單地通過其 ID 引用查詢。
模板接受在運行時指定參數(shù)。搜索模板存儲在服務(wù)器端,可以在不更改客戶端代碼的情況下進(jìn)行修改。
模板使用Mustache模板引擎表示。關(guān)于 Mustache 可以訪問:
http://mustache.github.io/mustache.5.html。
2.2 搜索模板舉例
根據(jù)第一部分實戰(zhàn)中的數(shù)據(jù),定義了如下的模板。
PUT?_scripts/cur_search_template
{
??"script":?{
????"lang":?"mustache",
????"source":?{
??????"query":?{
??????"match":?{
????????"{{cur_field}}":?"{{cur_value}}"
??????}
????},
????"size":?"{{cur_size}}"
????}
??}
}
POST?uint-*/_search/template
{
??"id":?"cur_search_template",
??"params":?{
????"cur_field":"itemid",
????"cur_value":1,
????"cur_size":50
????
??}
}
該模板:支持用戶自定義動態(tài)設(shè)置搜索字段及搜索參數(shù)字段。
實戰(zhàn)中可以通過如下_scripts 的方式,將檢索模板定義到服務(wù)器端。
如果想檢索別的字段:客戶端或者請求端傳遞不同的參數(shù)即可。
真正意義上的實現(xiàn)了:檢索和請求參數(shù)的分離。
更多原理和基礎(chǔ)參見官方文檔:
https://www.elastic.co/guide/en/elasticsearch/reference/current/search-template.html
2.3 search template 的語法很讓人頭腦
以下內(nèi)容摘自:Wood 大叔的——Elastic認(rèn)證考試心得。
按照要求寫一個search template 熟悉search template的mustache模版語言即可輕松寫出,但是很遺憾,平常沒用過search template,雖然知道個大概,但是當(dāng)時寫的時候,不知道哪里語法有問題,PUT template總是不成功。猜想可能是哪個位置的字符沒有轉(zhuǎn)譯產(chǎn)生非法json字符,或者哪一層嵌套有問題。總之就是調(diào)試不成功,又浪費了很多時間。
https://elasticsearch.cn/article/6133
如上引用想說明的是:search template的語法比較復(fù)雜,如果沒用過,很容易頭大。
3、問題拆解
3.1 原有DSL有錯嗎?
實戰(zhàn)一把,報錯如下:
{
??"error":?{
????"root_cause":?[
??????{
????????"type":?"parsing_exception",
????????"reason":?"[terms]?query?malformed,?no?start_object?after?query?name",
????????"line":?1,
????????"col":?67
??????}
????],
????"type":?"parsing_exception",
????"reason":?"[terms]?query?malformed,?no?start_object?after?query?name",
????"line":?1,
????"col":?67
??},
??"status":?400
}
3.2 哪里出了問題?
拆解一下。
script 部分無非包括:檢索部分和聚合部分。 檢索部分是定義 search template 的核心,聚合部分無需關(guān)注。
這個時候,可以寫一個檢索 DSL驗證一下是否ok,如下:
POST?/_search
{
??"_source":?[
????"value"
??],
??"size":?0,
??"query":?{
????"bool":?{
??????"filter":?[
????????{
??????????"terms":?{
????????????"itemid":?[
??????????????1,
??????????????2
????????????]
??????????}
????????},
????????{
??????????"range":?{
????????????"clock":?{
??????????????"gte":?1597752309,
??????????????"lte":?1597752333
????????????}
??????????}
????????}
??????]
????}
??},
??"aggs":?{
????"group_terms":?{
??????"terms":?{
????????"field":?"itemid"
??????},
??????"aggs":?{
????????"avg_value":?{
??????????"avg":?{
????????????"field":?"value"
??????????}
????????},
????????"max_value":?{
??????????"max":?{
????????????"field":?"value"
??????????}
????????}
??????}
????}
??}
}
而檢索和聚合都沒錯,那多半就是定義 search template 部分出錯了。
問題就這么一點點拆解了。
上來直接改這個 DSL貌似也無從下手,那咱們就做:最小化處理吧。
拋去所有:_source、size、aggs、range query 部分,只保留 terms 腳本應(yīng)該怎么正確的寫?
來吧,實戰(zhàn)一把:
第一步:最小化 terms 檢索模板。
GET?_search/template
{
??"source":?"{?\"query\":?{?\"terms\":?{{#toJson}}statuses{{/toJson}}?}}",
??"params":?{
????"statuses"?:?{
????????"itemid":?[?1,?2?]
????}
??}
}
用現(xiàn)在正確的對比第一部分出錯的,可以找到如下兩處錯誤:
錯誤1:source 里面的內(nèi)容要加:"\" 。 錯誤2:查詢模版參數(shù)中的 statuses 和 itemid 位置寫錯了。
官方文檔的說法:?
The {{#toJson}}parameter{{/toJson}} function can be used to convert parameters like maps and array to their JSON representation:
statuses 就是個輔助參數(shù),我們核心的參數(shù)是 itemid。
第二步:將第一步內(nèi)容轉(zhuǎn)成script 形式。
POST?_scripts/test_script_01
{
??"script":?{
????"lang":?"mustache",
????"source":?"{?\"query\":?{?\"terms\":?{{#toJson}}statuses{{/toJson}}?}}"
??}
}
POST?uint-*/_search/template?
{
??"id":?"test_script_01",
??"params":?{
????"statuses":?{
??????"itemid":?[
????????1,
????????2
??????]
????},
????"startTime":?1597752309,
????"endTime":?1597752333
??}
}
第三步:按照實戰(zhàn)要求補全參數(shù)即可。
注意補全的時候,我建議:拷貝 DSL(格式化一行的版本)到第三方文本工具如:Nodepad++,全局替換。

切記不要手敲,很容易出錯。
替換到模板的 source 部分,然后再根據(jù)第一步、第二步內(nèi)容修改即可。
實戰(zhàn)問題答案
GET?_search/template
{
??"source":?"{\"_source\":[\"value\"],\"size\":0,\"query\":{\"bool\":{\"filter\":[{\"terms\":{{#toJson}}statuses{{/toJson}}},{\"range\":{\"clock\":{\"gte\":{{startTime}},\"lte\":{{endTime}}}}}]}},\"aggs\":{\"group_terms\":{\"terms\":{\"field\":\"itemid\"},\"aggs\":{\"avg_value\":{\"avg\":{\"field\":\"value\"}},\"max_value\":{\"max\":{\"field\":\"value\"}}}}}}",
????"params":?{
????"statuses"?:?{
????????"itemid":?[?1,?2?]
????},
???????"startTime":1597752309,
????"endTime":1597752333
??}
}
拷貝 source 部分轉(zhuǎn)換為腳本格式就可以,篇幅問題,不再贅述。
4、小結(jié)
看似復(fù)雜,拆解后便不復(fù)雜。
看似很難,拆解后就很簡單。
檢索模板用的好,前后端扯皮少、效率高很多!
你的小問題,我的大問題。
和你一起,死磕 Elasticsearch!
參考:
https://elastic-search-in-action.medcl.com/3.site_search/3.3.search_box/search_template/
https://subscription.packtpub.com/book/big_data_and_business_intelligence/9781787128453/7/ch07lvl1sec61/search-templates
推薦:
更短時間更快習(xí)得更多干貨!
中國?40%+?Elastic 認(rèn)證工程師出自于此!
和全球?800+?Elastic 愛好者一起死磕 Elasticsearch!
加微信:elastic6,索要價值18元星球入場券
