Elasticsearch系列---幾個高階功能
阿新 • • 發佈:2020-05-16
### 概要
本篇主要介紹一下搜尋模板、對映模板、高亮搜尋和地理位置的簡單玩法。
### 標準搜尋模板
搜尋模板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": ["