ElasticSearch7.3 學習之訂製動態映射(dynamic mapping)
- 2022 年 3 月 24 日
- 筆記
- 【G】ElasticSearch
1、dynamic mapping
ElasticSearch
中有一個非常重要的特性——動態映射,即索引文檔前不需要創建索引、類型等資訊,在索引的同時會自動完成索引、類型、映射的創建。
當ES在文檔中碰到一個以前沒見過的欄位時,它會利用動態映射(dynamic mapping
)來決定該欄位的類型,並自動地對該欄位添加映射。
有時這正是需要的行為,但有時不是,需要留意。你或許不知道在以後你的文檔中會添加哪些欄位,但是你想要它們能夠被自動地索引。或許你只是想要忽略它們。或者,尤其當你將ES當做主要的數據存儲使用時,大概你會希望這些未知的欄位會拋出異常來提醒你注意這一問題。
幸運的是,你可以通過dynamic
設置來控制這一行為,它能夠接受以下的選項:
true
:默認值。動態添加欄位false
:新檢測到的欄位將被忽略。這些欄位將不會被索引,因此將無法搜索,但仍將出現在返回點擊的源欄位中。這些欄位不會添加到映射中,必須顯式添加新欄位。strict
:如果碰到陌生欄位,拋出異常
dynamic
設置可以適用在根對象上或者object
類型的任意欄位上。你應該默認地將dynamic
設置為strict
,但是為某個特定的內部對象啟用它:
創建mapping
PUT /my_index
{
"mappings": {
"dynamic": "strict",
"properties": {
"title": {
"type": "text"
},
"address": {
"type": "object",
"dynamic": "true"
}
}
}
插入數據
PUT /my_index/_doc/1
{
"title": "my article",
"content": "this is my article",
"address": {
"province": "guangdong",
"city": "guangzhou"
}
}
報錯,原因為content
為新增欄位。會拋出異常
{
"error": {
"root_cause": [
{
"type": "strict_dynamic_mapping_exception",
"reason": "mapping set to strict, dynamic introduction of [content] within [_doc] is not allowed"
}
],
"type": "strict_dynamic_mapping_exception",
"reason": "mapping set to strict, dynamic introduction of [content] within [_doc] is not allowed"
},
"status": 400
}
2、自定義 dynamic mapping策略
2.1 數據類型
如果你知道你需要動態的添加的新欄位,那麼你也許會啟用動態映射。然而有時動態映射的規則又有些不夠靈活。幸運的是,你可以調整某些設置來讓動態映射的規則更加適合你的數據。
es會根據傳入的值,推斷類型,具體如下表所示。
JSON data type |
Elasticsearch data type |
ES中的數據類型 |
|
No field is added. |
不會添加欄位 |
|
|
boolean |
floating point number |
|
double |
integer |
|
long |
object |
|
object |
array |
Depends on the first non- |
依賴於第一個非null得值 |
string |
Either a |
如果通過了date檢測,則為date 如果通過了numeric檢測,則為Number |
2.2 date_detection 日期探測
默認會按照一定格式識別date
,比如yyyy-MM-dd
。但是如果某個field
先過來一個2017-01-01
的值,就會被自動dynamic mapping
成date
,後面如果再來一個”hello world”之類的值,就會報錯。可以手動關閉某個type
的date_detection
,如果有需要,自己手動指定某個field
為date
類型。
首先刪除上面新建的索引
DELETE my_index
然後下面的語句代表的含義為:日期探測為false
,表示可添加不是date數據類型的欄位,也不會被推斷成date
類型。address
欄位裡面為true
,代表可動態往裡面添加新的欄位。
PUT /my_index
{
"mappings": {
"date_detection": false,
"properties": {
"title": {
"type": "text"
},
"address": {
"type": "object",
"dynamic": "true"
}
}
}
}
測試插入數據。下面的語句代表最外層級新增content
、post_date
兩個欄位,address
層級新增province
、city
欄位。
PUT /my_index/_doc/1
{
"title": "my article",
"content": "this is my article",
"address": {
"province": "guangdong",
"city": "guangzhou"
},
"post_date": "2019-09-10"
}
查看映射
GET /my_index/_mapping
返回,可以看到欄位都成功新增,日期檢測(date_detection
)為false
,所以post_date
欄位類型不是date
類型。
{
"my_index" : {
"mappings" : {
"date_detection" : false,
"properties" : {
"address" : {
"dynamic" : "true",
"properties" : {
"city" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"province" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
}
}
},
"content" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"post_date" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"title" : {
"type" : "text"
}
}
}
}
}
下面為自定義日期格式語法,讀者可自行試驗。
PUT my_index
{
"mappings": {
"dynamic_date_formats": ["MM/dd/yyyy"]
}
}
插入數據
PUT my_index/_doc/1
{
"create_date": "09/25/2019"
}
2.3 numeric_detection 數字探測
雖然json
支援浮點和整數數據類型,但某些應用程式或語言有時可能需要將數字呈現為字元串。通常正確的解決方案是顯式地映射這些欄位,但是可以啟用數字檢測(默認情況下禁用)來自動完成這些操作。
首先刪除上面新建的索引
DELETE my_index
然後開啟數字檢測
PUT my_index
{
"mappings": {
"numeric_detection": true
}
}
插入數據
PUT my_index/_doc/1
{
"my_float": "1.0",
"my_integer": "1"
}
查看映射
GET my_index/_mapping
返回
{
"my_index" : {
"mappings" : {
"numeric_detection" : true,
"properties" : {
"my_float" : {
"type" : "float"
},
"my_integer" : {
"type" : "long"
}
}
}
}
}
可以看到兩個欄位被映射為浮點類型了。
3、訂製dynamic mapping template
通過dynamic_templates
,你可以擁有對新欄位的動態映射規則擁有完全的控制。你設置可以根據欄位名稱或者類型來使用一個不同的映射規則。
每個模板都有一個名字,可以用來描述這個模板做了什麼。同時它有一個mapping
用來指定具體的映射資訊,和至少一個參數(比如match
)用來規定對於什麼欄位需要使用該模板。
首先刪除上面新建的索引
DELETE my_index
運行下面的語句,代表的含義為:匹配以_en
結尾並且是string
類型的欄位,設置它的type
為text
,使用english
分詞。
PUT /my_index
{
"mappings": {
"dynamic_templates": [
{
"en": {
"match": "*_en",
"match_mapping_type": "string",
"mapping": {
"type": "text",
"analyzer": "english"
}
}
}
]
}
}
插入數據
PUT /my_index/_doc/1
{
"title": "this is my first article"
}
PUT /my_index/_doc/2
{
"title_en": "this is my first article"
}
查看映射
GET my_index/_mapping
返回,可以看到title_en
欄位採用english
分詞器,說明模板生效了。
{
"my_index" : {
"mappings" : {
"dynamic_templates" : [
{
"en" : {
"match" : "*_en",
"match_mapping_type" : "string",
"mapping" : {
"analyzer" : "english",
"type" : "text"
}
}
}
],
"properties" : {
"title" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"title_en" : {
"type" : "text",
"analyzer" : "english"
}
}
}
}
}
搜索
GET my_index/_search?q=first
GET my_index/_search?q=is
title
欄位沒有匹配到任何的dynamic
模板,默認就是standard
分詞器,不會過濾停用詞,is
會進入倒排索引,用is
來搜索是可以搜索到的
title_en
欄位匹配到了dynamic
模板,就是english
分詞器,會過濾停用詞,is
這種停用詞就會被過濾掉,用is
來搜索就搜索不到了
4、模板寫法
下面給出了一些大概的寫法,讀者可根據自身實際需求自定義模板
PUT my_index
{
"mappings": {
"dynamic_templates": [
{
"integers": {
"match_mapping_type": "long",
"mapping": {
"type": "integer"
}
}
},
{
"strings": {
"match_mapping_type": "string",
"mapping": {
"type": "text",
"fields": {
"raw": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
}
]
}
}
模板參數:匹配滿足的欄位、不匹配滿足的欄位、匹配的數據類型、路徑匹配、路徑不匹配。
"match": "long_*",
"unmatch": "*_text",
"match_mapping_type": "string",
"path_match": "name.*",
"path_unmatch": "*.middle",
"match_pattern": "regex",
"match": "^profit_\d+$"
5、應用場景
5.1 結構化搜索
默認情況下,elasticsearch
將字元串欄位映射為帶有子關鍵字(keyword
)欄位的文本欄位。但是,如果只對結構化內容進行索引,而對全文搜索不感興趣,則可以僅將「欄位」映射為「關鍵字」。但是請注意,這意味著為了搜索這些欄位,必須搜索索引所用的完全相同的值。
{
"strings_as_keywords": {
"match_mapping_type": "string",
"mapping": {
"type": "keyword"
}
}
}
5.2 僅搜索
與前面的示例相反,如果您只關心字元串欄位的全文搜索,並且不打算對字元串欄位運行聚合、排序或精確搜索,您可以將其僅映射為文本欄位(這是es5
之前的默認行為)
{
"strings_as_text": {
"match_mapping_type": "string",
"mapping": {
"type": "text"
}
}
}
5.3 norms 不關心評分
當計算得分的時候,是否需要把欄位長度用作參數計算。
儘管計算得分時把欄位長度考慮在內可以提高得分的精確性,但這樣會消耗大量的磁碟空間(每個文檔的每個欄位都會消耗一個位元組,即使某些文檔不包含這個欄位)。因此,如果不需要計算欄位的得分,你應該禁用該欄位的norms
。特別是這個欄位僅用於聚合或者過濾。
{
"properties": {
"title": {
"type": "text",
"norms": false
}
}
}