什麼是Elasticsearch的動態映射? 它有什麼作用和優點? 如何自定義使用動態模板? 本篇文章介紹這些內容. ...
目錄
1 動態映射(dynamic mapping)
1.1 什麼是動態映射
動態映射時Elasticsearch的一個重要特性: 不需要提前創建iindex、定義mapping信息和type類型, 你可以 直接向ES中插入文檔數據時, ES會根據每個新field可能的數據類型, 自動為其配置type等mapping信息, 這個過程就是動態映射(dynamic mapping).
Elasticsearch動態映射的示例:
欄位內容(field) | 映射的欄位類型(type) |
---|---|
true | false | boolean |
1234 | long |
123.4 | float |
2018-10-10 | date |
"hello world" | text |
說明: 動態映射雖然方便, 可並不直觀, 為了個性化自定義相關設置, 可以在添加文檔之前, 先創建index和type, 並配置type對應的mapping, 以取代動態映射.
mapping的配置可參考博文: ES 11 - 配置Elasticsearch的映射(mapping).
1.2 體驗動態映射
(1) 插入如下數據:
如果已有website索引, 先刪除再執行下麵的插入操作:
DELETE website
.
PUT website/blog/1
{
"blog_id": 10001,
"author_id": 5520,
"post_date": "2018-01-01",
"title": "my first blog",
"content": "my first blog in the website"
}
PUT website/blog/2
{
"blog_id": 10002,
"author_id": 5520,
"post_date": "2018-01-02",
"title": "my second blog",
"content": "my second blog in the website"
}
PUT website/blog/3
{
"blog_id": 10003,
"author_id": 5520,
"post_date": "2018-01-03",
"title": "my third blog",
"content": "my third blog in the website"
}
(2) 進行如下檢索測試:
註意這裡結果數是3的情況.
GET website/blog/_search?q=2018 // 1條結果, 5.6之前的版本是3條結果
GET website/blog/_search?q=2018-01-01 // 1條結果, 5.6之前的版本是3條結果
GET website/blog/_search?q=post_date:2018 // 1條結果
GET website/blog/_search?q=post_date:2018-01-01 // 1條結果
(3) 查看ES自動建立的mapping:
GET website/_mapping
// 結果如下:
{
"website" : { // index是website
"mappings" : {
"blog" : { // type是blog
"properties" : {
"author_id" : {
"type" : "long"
},
"blog_id" : {
"type" : "long"
},
"content" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"post_date" : {
"type" : "date" // 發佈日期"2018-01-01"被自動識別為date類型
},
"title" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
}
}
}
}
}
}
1.3 搜索結果不一致的原因分析
如果使用的是較早的版本, 以5.6.x為例, [1.2]節中搜索結果會不一致, 這是因為:
ES自動建立mapping時, 為不同的field映射了不同的數據類型, 而不同數據類型在分詞、搜索等行為上也是不同的.
官方文檔指出, 從6.0+版本開始,
_all
欄位的設置已經禁止使用, 建議我們使用copy_to
實現相似的功能.—— 也就是說, 如果_all
欄位被關閉, 就不會出現搜索結果不一致的情況.
(1) GET website/blog/_search?q=2018
ES預設將每個文檔的所有field的值抽取到一個名為
_all
的元欄位中, 如id=1的文檔的_all
的值為:2018-01-01 my first blog my first blog in the website 5520
說明:
_all
欄位將所有的值作為字元串索引, 所以日期被索引為年、月、日三個值,_all
欄位的倒排索引結果如下:
doc1 doc2 Doc3 2018 * * * 01 * * * 02 * 03 * 此項搜索中, ES是從
_all
欄位中檢索, 3個文檔中都有2018
, 所以結果數是3.
(2) GET website/blog/?q=2018-01-01
同(1), ES也是從
_all
欄位中檢索, 結果數同樣是3.
(3) GET website/blog/_search?q=post_date:2018-01-01
此檢索指定了檢索條件, ES將從post_date欄位中檢索, 而post_date被映射為date類型, 所以將進行精確匹配.
而date類型的欄位索引的內容有其預設的固定格式.
post_date
欄位的倒排索引結果如下:
doc1 doc2 doc3 2018-01-01 00:00:00 UTC * 2018-01-02 00:00:00 UTC * 2018-01-03 00:00:00 UTC * 可以看出, 滿足條件的只有1個結果, 即doc1.
(4) GET /_search?q=post_date:2018
這是ES 5.x版本中做的一個優化, 搜索
post_date:01
等是不會出現結果的, 搜索2018會出現第一條結果.
2 開啟dynamic mapping策略
2.1 約束策略
策略 | 功能說明 |
---|---|
true |
開啟 —— 遇到陌生欄位時, 進行動態映射 |
false |
關閉 —— 忽略遇到的陌生欄位 |
strict |
遇到陌生欄位時, 作報錯處理 |
2.2 策略示例
(1) 使用不同的約束策略:
PUT user
{
"mappings": {
"blog_user": {
"dynamic": "strict", // 嚴格控制策略
"properties": {
"name": { "type": "text" },
"address": {
"type": "object",
"dynamic": "true" // 開啟動態映射策略
}
}
}
}
}
(2) 插入數據演示:
// 插入數據時多添加一個欄位
PUT user/blog_user/1
{
"name": "shou feng",
"content": "this is my blog", // 多添加的欄位
"address": {
"province": "guangdong", // 多添加的欄位
"city": "guangzhou" // 多添加的欄位
}
}
將拋出如下錯誤信息:
{
"error": {
"root_cause": [
{
"type": "strict_dynamic_mapping_exception",
// 錯誤原因: 不允許動態添加field
"reason": "mapping set to strict, dynamic introduction of [content] within [blog_user] is not allowed"
}
],
"type": "strict_dynamic_mapping_exception",
"reason": "mapping set to strict, dynamic introduction of [content] within [blog_user] is not allowed"
},
"status": 400
}
添加符合約束的數據, 操作就能成功:
PUT user/blog_user/1
{
"name": "shou feng",
"address": {
"province": "guangdong",
"city": "guangzhou"
}
}
(3) 查看映射信息:
GET user/_mapping
// 映射信息如下:
{
"user" : {
"mappings" : {
"blog_user" : {
"dynamic" : "strict", // 嚴格約束條件
"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
}
}
}
}
},
"name" : {
"type" : "text"
}
}
}
}
}
}
3 定製dynamic mapping策略
3.1 date_detection - 日期識別策略
對於date類型的數據, Elasticsearch有其預設的識別策略, 比如"yyyy-MM-dd". 存在這種情況:
① 第一次添加文檔時, 某個field是類似"2018-01-01"的值, 其類型就被動態映射成了date;
② 後期再次添加文檔, 該field是類似"hello world"的值, ES就會因為類型不匹配而報錯.
為解決這一問題, 可以手動關閉某個type的date_detection; 如果不需要關閉, 建議手動指定這個field為date類型.
關閉示例如下:
PUT user/_mapping/blog_user
{
"date_detection": false
}
3.2 在type中自定義動態映射模板
(1) 在type中定義動態映射模板(dynamic mapping template) —— 把所有的string類型映射成text和keyword類型:
先刪除已有的
user
索引:DELETE user
, 再執行下述命令:
PUT user
{
"mappings": {
"blog_user": {
"dynamic_templates": [
{
"en": {
"match": "*_en", // 匹配"*_en"的field
"match_mapping_type": "string",
"mapping": {
"type": "text", // 把所有的string類型, 映射成text類型
"analyzer": "english", // 使用english分詞器
"fields": {
"raw": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
}
]
}
}
}
(2) 添加數據:
PUT user/blog_user/1
{
"name": "the first register user"
}
PUT user/blog_user/2
{
"name_en": "the second register user"
}
(3) 檢索數據:
// 有結果: "name"沒有匹配到任何動態模板, 預設使用standard分詞器
GET user/_search
{
"query": {
"match": {"name": "the"}
}
}
// 無結果: "name_en"匹配到了動態模板, 使用english分詞器, the是停用詞, 被過濾掉了
GET user/_search
{
"query": {
"match": {"name_en": "the"}
}
}
說明:
這裡的
match_mapping_type
的類型支持[object, string, long, double, boolean, date, binary]
, 若使用text
將拋出如下錯誤信息:{ "error": { "root_cause": [ { "type": "mapper_parsing_exception", "reason": "Failed to parse mapping [blog_user]: No field type matched on [text], possible values are [object, string, long, double, boolean, date, binary]" } ], "type": "mapper_parsing_exception", "reason": "Failed to parse mapping [blog_user]: No field type matched on [text], possible values are [object, string, long, double, boolean, date, binary]", "caused_by": { "type": "illegal_argument_exception", "reason": "No field type matched on [text], possible values are [object, string, long, double, boolean, date, binary]" } }, "status": 400 }
在6.0之前的版本, 將拋出如下過期的警告信息:
Deprecation: match_mapping_type [text] is invalid and will be ignored: No field type matched on [text], possible values are [object, string, long, double, boolean, date, binary]
3.3 [過期]在index中自定義預設映射模板
_default mapping
- 預設映射模板是類似於全局變數的存在, 對當前配置的索引起作用.
預設映射模板在Elasticsearch 6.x版本中已經不再支持, 因為6.0版本開始, 每個索引只能有一個類型, 因此預設模板沒有存在的意義了.
下麵的演示過程, 是在6.0之前的版本上的測試.
(1) 在index中定義預設映射模板(default mapping template):
先刪除已有的
user
索引:DELETE user
, 再執行下述命令:
PUT user
{
"mappings": {
"_default_": {
"_all": { "enabled": false }
},
"blog_user": {
"_all": { "enabled": true }
}
}
}
(2) 動態映射可以與索引模板(Index Templates)配合使用.
無論是自動創建Index, 還是手動顯示創建Index, 索引模板都可以用來配置新索引的預設mappings(映射), settings(配置項)和aliases(別名). 具體使用方法請參考博客: ES 10 - Elasticsearch的索引別名和索引模板
參考資料
版權聲明
作者: ma_shoufeng(馬瘦風)
出處: 博客園 馬瘦風的博客
您的支持是對博主的極大鼓勵, 感謝您的閱讀.
本文版權歸博主所有, 歡迎轉載, 但請保留此段聲明, 併在文章頁面明顯位置給出原文鏈接, 否則博主保留追究相關人員法律責任的權利.