1. 程式人生 > >Elasticsearch系列---幾個高階功能

Elasticsearch系列---幾個高階功能

### 概要 本篇主要介紹一下搜尋模板、對映模板、高亮搜尋和地理位置的簡單玩法。 ### 標準搜尋模板 搜尋模板search tempalte高階功能之一,可以將我們的一些搜尋進行模板化,使用現有模板時傳入指定的引數就可以了,避免編寫重複程式碼。對常用的功能可以利用模板進行封裝,使用時更簡便。 這點類似於我們程式設計時的介面封裝,將一些細節處理的東西封裝成介面,供別人呼叫,使用者就只需要關注引數和響應結果就行,這樣可以更好地提高程式碼複用率。 下面我們來看看最基本的幾種用法 #### 引數替換 ```java GET /music/children/_search/template { "source": { "query": { "match": { "{{field}}":"{{value}}" } } }, "params": { "field":"name", "value":"bye-bye" } } ``` 該搜尋模板編譯後等同於: ```java GET /music/children/_search { "query": { "match": { "name":"bye-bye" } } } ``` #### 使用Json格式的條件查詢 {{#toJson}}塊內可以寫稍微複雜一些的條件 ```java GET /music/children/_search/template { "source": "{\"query\":{\"match\": {{#toJson}}condition{{/toJson}}}}", "params": { "condition": { "name":"bye-bye" } } } ``` 該搜尋模板編譯後等同於如下: ```java GET /music/children/_search { "query": { "match": { "name":"bye-bye" } } } ``` #### join語法 join內的引數names可以寫多個: ```java GET /music/children/_search/template { "source": { "query": { "match": { "name": "{{#join delimiter=' '}}names{{/join delimiter=' '}}" } } }, "params": { "name":["gymbo","you are my sunshine","bye-bye"] } } ``` 該搜尋模板編譯後等同於如下: ```java GET /music/children/_search { "query": { "match": { "name":"gymbo you are my sunshine bye-bye" } } } ``` #### 搜尋模板的預設值設定 可以對搜尋模板進行一些預設值的設定,如{{^end}}500表示如果end引數為空,預設值為500 ```java GET /music/children/_search/template { "source":{ "query":{ "range":{ "likes":{ "gte":"{{start}}", "lte":"{{end}}{{^end}}500{{/end}}" } } } }, "params": { "start":1, "end":300 } } ``` 該搜尋模板編譯後等同於: ```java GET /music/children/_search { "query": { "range": { "likes": { "gte": 1, "lte": 300 } } } } ``` #### 條件判斷 在Mustache語言中,它沒有if/else這樣的判斷,但是你可以定section來跳過它如果那個變數是false還是沒有被定義 ```java {{#param1}} "This section is skipped if param1 is null or false" {{/param1}} ``` 示例:建立mustache scripts物件 ```java POST _scripts/condition { "script": { "lang": "mustache", "source": """ { "query": { "bool": { "must": { "match": { "name": "{{name}}" } }, "filter":{ {{#isLike}} "range":{ "likes":{ {{#start}} "gte":"{{start}}" {{#end}},{{/end}} {{/start}} {{#end}} "lte":"{{end}}" {{/end}} } } {{/isLike}} } } } } """ } } ``` 使用mustache template查詢: ```java GET _search/template { "id": "condition", "params": { "name":"gymbo", "isLike":true, "start":1, "end":500 } } ``` 以上是常用的幾種搜尋模板介紹,如果在大型專案,並且配置了專門的Elasticsearch工程師,就經常會用一些通用的功能進行模板化,開發業務系統的童鞋只需要使用模板即可。 ### 定製對映模板 ES有自己的規則對插入的資料進行型別對映,如10,會自動對映成long型別,"10"會自動對映成text,還會自帶一個keyword的內建field。方便是很方便,但有時候這些型別不是我們想要的,比如我們的整數值10,我們期望是這個integer型別,"10"我們希望是keyword型別,這時候我們可以預先定義一個模板,插入資料時,相關的field就按我們預先定義的規則進行匹配,決定這個field值的型別。 另外要宣告一下,實際工作中編碼規範一般嚴謹一些,所有的document都是預先定義好型別再執行資料插入的,哪怕是中途增加的field,也是先執行mapping命令,再插入資料的。 但自定義動態對映模板也需要了解一下。 #### 預設的動態對映效果 試著插入一條資料: ```java PUT /test_index/type/1 { "test_string":"hello kitty", "test_number":10 } ``` 檢視mapping資訊 `GET /test_index/_mapping/type` 響應如下: ```java { "test_index": { "mappings": { "type": { "properties": { "test_number": { "type": "long" }, "test_string": { "type": "text", "fields": { "keyword": { "type": "keyword", "ignore_above": 256 } } } } } } } } ``` 預設的動態對映規則,可能不是我們想要的。 例如,我們希望數字型別的預設是integer型別,字串預設是string型別,但是內建的field名字叫raw,不叫keyword,保留128個字元。 #### 動態對映模板 有兩種方式: 1. 根據新加入的field的預設的資料型別,來進行匹配,匹配某個預定義的模板 2. 根據新加入的field的名字,去匹配預定義的名字,或者去匹配一個預定義的萬用字元,然後匹配上某個預定義的模板 ##### 根據資料型別進行匹配 ```java PUT /test_index { "mappings": { "type": { "dynamic_templates": [ { "integers" : { "match_mapping_type": "long", "mapping": { "type":"integer" } } }, { "strings" : { "match_mapping_type": "string", "mapping": { "type":"text", "fields": { "raw": { "type": "keyword", "ignore_above": 128 } } } } } ] } } } ``` 刪除索引,重新插入資料,檢視mapping資訊如下: ```java { "test_index": { "mappings": { "type": { "dynamic_templates": [ { "integers": { "match_mapping_type": "long", "mapping": { "type": "integer" } } }, { "strings": { "match_mapping_type": "string", "mapping": { "fields": { "raw": { "ignore_above": 128, "type": "keyword" } }, "type": "text" } } } ], "properties": { "test_number": { "type": "integer" }, "test_string": { "type": "text", "fields": { "raw": { "type": "keyword", "ignore_above": 128 } } } } } } } } ``` 以按預計型別進行對映,符合預期。 - 按field名稱進行對映 - "long_"開頭的field,並且原本是long型別的,轉換為integer型別 - "string_"開頭的field,並且原本是string型別的,轉換為string.raw型別 "_text"結尾的field,並且原本是string型別的,保持不變 ```java PUT /test_index { "mappings": { "type": { "dynamic_templates":[ { "long_as_integer": { "match_mapping_type":"long", "match": "long_*", "mapping":{ "type":"integer" } } }, { "string_as_raw": { "match_mapping_type":"string", "match": "string_*", "unmatch":"*_text", "mapping": { "type":"text", "fields": { "raw": { "type": "keyword", "ignore_above": 128 } } } } } ] } } } ``` 插入資料: ```java PUT /test_index/type/1 { "string_test":"hello kitty", "long_test": 10, "title_text":"Hello everyone" } ``` 查詢mapping資訊 ```java { "test_index": { "mappings": { "type": { "dynamic_templates": [ { "long_as_integer": { "match": "long_*", "match_mapping_type": "long", "mapping": { "type": "integer" } } }, { "string_as_raw": { "match": "string_*", "unmatch": "*_text", "match_mapping_type": "string", "mapping": { "fields": { "raw": { "ignore_above": 128, "type": "keyword" } }, "type": "text" } } } ], "properties": { "long_test": { "type": "integer" }, "string_test": { "type": "text", "fields": { "raw": { "type": "keyword", "ignore_above": 128 } } }, "title_text": { "type": "text", "fields": { "keyword": { "type": "keyword", "ignore_above": 256 } } } } } } } } ``` 結果符合預期。 在某些日誌管理的場景中,我們可以定義好type,每天按日期建立一個索引,這種索引的建立就可以用到對映模板,把我們定義的對映關係全部做進去。 ### 高亮搜尋 我們在瀏覽器上搜索文字時,發現我們輸入的關鍵字有高亮顯示,檢視html原始碼就知道,高亮的部分是加了``標籤的,ES也支援高亮搜尋這種操作的,並且在返回的文件中自動加了``標籤,相容html5頁面。 #### highlight基本語法 我們還是以音樂網站為案例,開始進行高亮搜尋: ```java GET /music/children/_search { "query": { "match": { "content": "love" } }, "highlight": { "fields": { "content": {} } } } ``` highlight裡面的引數即為高亮搜尋的語法,指定高亮的欄位為content,我們可以看到命中的Love裡面帶了``高亮標籤,`
`表現在html上會變成紅色,所以說你的指定的field中,如果包含了那個搜尋詞的話,就會在那個field的文字中,對搜尋詞進行紅色的高亮顯示。 ```java { "took": 35, "timed_out": false, "_shards": { "total": 5, "successful": 5, "skipped": 0, "failed": 0 }, "hits": { "total": 1, "max_score": 0.2876821, "hits": [ { "_index": "music", "_type": "children", "_id": "5", "_score": 0.2876821, "_source": { "id": "1740e61c-63da-474f-9058-c2ab3c4f0b0a", "author_first_name": "Jean", "author_last_name": "Ritchie", "author": "Jean Ritchie", "name": "love somebody", "content": "love somebody, yes I do", "language": "english", "tags": "love", "length": 38, "likes": 3, "isRelease": true, "releaseDate": "2019-12-22" }, "highlight": { "content": [ "love
somebody, yes I do" ] } } ] } } ``` highlight下的欄位可以指定多個,這樣就可以在多個欄位命中的關鍵詞進行高亮顯示,例如: ```java GET /music/children/_search { "query": { "match": { "content": "love" } }, "highlight": { "fields": { "name":{}, "content": {} } } } ``` #### 三種高亮語法 有三種高亮的語法: 1. plain highlight:使用standard Lucene highlighter,對簡單的查詢支援度非常好。 2. unified highlight:預設的高亮語法,使用Lucene Unified Highlighter,將文字切分成句子,並對句子使用BM25計算詞條的score,支援精準查詢和模糊查詢。 3. fast vector highlighter:使用Lucene Fast Vector highlighter,功能很強大,如果在mapping中對field開啟了term_vector,並設定了with_positions_offsets,就會使用該highlighter,對內容特別長的文字(大於1MB)有效能上的優勢。 例如: ```java PUT /music { "mappings": { "children": { "properties": { "name": { "type": "text", "analyzer": "ik_max_word" }, "content": { "type": "text", "analyzer": "ik_max_word", "term_vector" : "with_positions_offsets" } } } } } ``` 一般情況下,用plain highlight也就足夠了,不需要做其他額外的設定 如果對高亮的效能要求很高,可以嘗試啟用unified highlight 如果field的值特別大,超過了1M,那麼可以用fast vector highlight #### 自定義高亮html標籤 我們知道高亮的預設標籤是``,這個標籤可以自己定義的,然後使用自己喜歡的樣式: ```java GET /music/children/_search { "query": { "match": { "content": "Love" } }, "highlight": { "pre_tags": ["