
Elasticsearch做模版查詢的時候,在使用 terms 進行批量查詢的時候放入數組在模版中進行查詢失敗,類似于模版傳入數組該如何實作?
問題來源:死磕Elasticsearch知識星球
# 定義索引
PUT uint-2020-08-17
{
"mappings": {
"properties": {
"clock": {
"type": "date",
"format": "epoch_second"
},
"itemid": {
"type": "long"
"ns": {
"ttl": {
"value": {
}
}
}
}
# 添加内容
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": {
}
}
查詢模版參數:
POST uint-*/_search/template
"id": "item_agg",
"params": {
"itemid":{
"statuses":[1,2]
},
"startTime":1597752309,
"endTime":1597752333
以上内容看着很長,根據注釋拆解為:
定義索引、
插入資料、
建立模闆、
構造參數檢索
四個子部分你就不會恐慌了。
2、知識點解讀——搜尋模闆
2.1 什麼是搜尋模闆?
很多人都聽說使用過 索引模闆 index template,索引模闆的好處:
便于跨索引統一模組化;
尤其适合資料量巨大、索引字段類似的業務系統;
靈活便捷。
檢索模闆(search template)大家使用相對較少,在實戰業務場景中:每次業務請求都要構造 DSL,比如:這次查title、下次查content,除此之外的 DSL 部分 都一樣,但兩次請求:後端代碼那裡就要有相應的修改和适配。有沒有不修改、拼接DSL使用檢索的方案?這就引出了搜尋模闆。
搜尋模闆與關系資料庫中的存儲過程非常相似。可以将常用查詢定義為模闆,并且使用 Elasticsearch 的應用程式可以簡單地通過其 ID 引用查詢。
模闆接受在運作時指定參數。搜尋模闆存儲在伺服器端,可以在不更改用戶端代碼的情況下進行修改。
模闆使用Mustache模闆引擎表示。關于 Mustache 可以通路:
http://mustache.github.io/mustache.5.html。
2.2 搜尋模闆舉例
根據第一部分實戰中的資料,定義了如下的模闆。
PUT _scripts/cur_search_template
"match": {
"{{cur_field}}": "{{cur_value}}"
"size": "{{cur_size}}"
"id": "cur_search_template",
"cur_field":"itemid",
"cur_value":1,
"cur_size":50
該模闆:支援使用者自定義動态設定搜尋字段及搜尋參數字段。
實戰中可以通過如下_scripts 的方式,将檢索模闆定義到伺服器端。
如果想檢索别的字段:用戶端或者請求端傳遞不同的參數即可。
真正意義上的實作了:檢索和請求參數的分離。
更多原理和基礎參見官方文檔:
https://www.elastic.co/guide/en/elasticsearch/reference/current/search-template.html2.3 search template 的文法很讓人頭腦
以下内容摘自:Wood 大叔的——Elastic認證考試心得。
按照要求寫一個search template
熟悉search template的mustache模版語言即可輕松寫出,但是很遺憾,平常沒用過search template,雖然知道個大概,但是當時寫的時候,不知道哪裡文法有問題,PUT template總是不成功。猜想可能是哪個位置的字元沒有轉譯産生非法json字元,或者哪一層嵌套有問題。總之就是調試不成功,又浪費了很多時間。
https://elasticsearch.cn/article/6133如上引用想說明的是:search template的文法比較複雜,如果沒用過,很容易頭大。
3、問題拆解
3.1 原有DSL有錯嗎?
實戰一把,報錯如下:
"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 的核心,聚合部分無需關注。
這個時候,可以寫一個檢索 DSL驗證一下是否ok,如下:
POST /_search
"_source": [
"value"
],
"size": 0,
"query": {
"bool": {
"filter": [
{
"itemid": [
1,
2
]
"range": {
"clock": {
"gte": 1597752309,
"lte": 1597752333
]
"aggs": {
"group_terms": {
"terms": {
"field": "itemid"
"avg_value": {
"avg": {
"field": "value"
"max_value": {
"max": {
而檢索和聚合都沒錯,那多半就是定義 search template 部分出錯了。
問題就這麼一點點拆解了。
上來直接改這個 DSL貌似也無從下手,那咱們就做:最小化處理吧。
抛去所有:_source、size、aggs、range query 部分,隻保留 terms 腳本應該怎麼正确的寫?
來吧,實戰一把:
第一步:最小化 terms 檢索模闆。
GET _search/template
"source": "{ \"query\": { \"terms\": {{#toJson}}statuses{{/toJson}} }}",
"statuses" : {
"itemid": [ 1, 2 ]
用現在正确的對比第一部分出錯的,可以找到如下兩處錯誤:
錯誤1:source 裡面的内容要加:"\" 。
錯誤2:查詢模版參數中的 statuses 和 itemid 位置寫錯了。
官方文檔的說法:
The {undefined{#toJson}}parameter{undefined{/toJson}} function can be used to convert parameters like maps and array to their JSON representation:
statuses 就是個輔助參數,我們核心的參數是 itemid。
第二步:将第一步内容轉成script 形式。
POST _scripts/test_script_01
"source": "{ \"query\": { \"terms\": {{#toJson}}statuses{{/toJson}} }}"
POST uint-*/_search/template
"id": "test_script_01",
"statuses": {
"itemid": [
1,
2
"startTime": 1597752309,
"endTime": 1597752333
第三步:按照實戰要求補全參數即可。
注意補全的時候,我建議:拷貝 DSL(格式化一行的版本)到第三方文本工具如:Nodepad++,全局替換。
切記不要手敲,很容易出錯。
替換到模闆的 source 部分,然後再根據第一步、第二步内容修改即可。
實戰問題答案
"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": {
"startTime":1597752309,
拷貝 source 部分轉換為腳本格式就可以,篇幅問題,不再贅述。
4、小結
看似複雜,拆解後便不複雜。
看似很難,拆解後就很簡單。
檢索模闆用的好,前後端扯皮少、效率高很多!
你的小問題,我的大問題。
和你一起,死磕 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推薦:
重磅 | 死磕 Elasticsearch 方法論認知清單(2020年國慶更新版)
能拿駕照就能通過 Elastic 認證考試!