一文搞懂 Elasticsearch 之 Mapping

這篇文章主要介紹 Mapping、Dynamic Mapping 以及 ElasticSearch 是如何自動判斷字段的類型,同時介紹 Mapping 的相關參數設置。html

首先來看下什麼是 Mapping:java

什麼是 Mapping?

一篇文章帶你搞定 ElasticSearch 術語中,咱們講到了 Mapping 相似於數據庫中的表結構定義 schema,它有如下幾個做用:數據庫

  • 定義索引中的字段的名稱
  • 定義字段的數據類型,好比字符串、數字、布爾
  • 字段,倒排索引的相關配置,好比設置某個字段爲不被索引、記錄 position 等

在 ES 早期版本,一個索引下是能夠有多個 Type 的,從 7.0 開始,一個索引只有一個 Type,也能夠說一個 Type 有一個 Mapping 定義。數組

在瞭解了什麼是 Mapping 以後,接下來對 Mapping 的設置作下介紹:app

Mapping 設置

PUT users
{
	"mappings": {
		"_doc": {
			"dynamic": false
		}
	}
}

在建立一個索引的時候,能夠對 dynamic 進行設置,能夠設成 falsetrue 或者 strictelasticsearch

Dynamic Mappings 設置

好比一個新的文檔,這個文檔包含一個字段,當 Dynamic 設置爲 true 時,這個文檔能夠被索引進 ES,這個字段也能夠被索引,也就是這個字段能夠被搜索,Mapping 也同時被更新;當 dynamic 被設置爲 false 時候,存在新增字段的數據寫入,該數據能夠被索引,可是新增字段被丟棄;當設置成 strict 模式時候,數據寫入直接出錯。ide

另外還有 index 參數,用來控制當前字段是否被索引,默認爲 true,若是設爲 false,則該字段不可被搜索。學習

參數 index_options 用於控制倒排索引記錄的內容,有以下 4 種配置:ui

  • doc:只記錄 doc id
  • freqs:記錄 doc idterm frequencies
  • positions:記錄 doc idterm frequenciesterm position
  • offsets:記錄 doc idterm frequenciesterm positioncharacter offects

另外,text 類型默認配置爲 positions,其餘類型默認爲 doc,記錄內容越多,佔用存儲空間越大。編碼

null_value 主要是當字段遇到 null 值時的處理策略,默認爲 NULL,即空值,此時 ES 會默認忽略該值,能夠經過設定該值設定字段的默認值,另外只有 KeyWord 類型支持設定 null_value

copy_to 做用是將該字段的值複製到目標字段,實現相似 _all 的做用,它不會出如今 _source 中,只用來搜索。

除了上述介紹的參數,還有許多參數,你們感興趣的能夠在官方文檔中進行查看。

在學習了 Mapping 的設置以後,讓咱們來看下字段的數據類型有哪些吧!

字段數據類型

ES 字段類型相似於 MySQL 中的字段類型,ES 字段類型主要有:核心類型、複雜類型、地理類型以及特殊類型,具體的數據類型以下圖所示:

字段數據類型

核心類型

從圖中能夠看出核心類型能夠劃分爲字符串類型、數字類型、日期類型、布爾類型、基於 BASE64 的二進制類型、範圍類型。

字符串類型

其中,在 ES 7.x 有兩種字符串類型:textkeyword,在 ES 5.x 以後 string 類型已經再也不支持了。

text 類型適用於須要被全文檢索的字段,例如新聞正文、郵件內容等比較長的文字,text 類型會被 Lucene 分詞器(Analyzer)處理爲一個個詞項,並使用 Lucene 倒排索引存儲,text 字段不能被用於排序,若是須要使用該類型的字段只須要在定義映射時指定 JSON 中對應字段的 typetext

keyword 適合簡短、結構化字符串,例如主機名、姓名、商品名稱等,能夠用於過濾、排序、聚合檢索,也能夠用於精確查詢

數字類型

數字類型分爲 long、integer、short、byte、double、float、half_float、scaled_float

數字類型的字段在知足需求的前提下應當儘可能選擇範圍較小的數據類型,字段長度越短,搜索效率越高,對於浮點數,能夠優先考慮使用 scaled_float 類型,該類型能夠經過縮放因子來精確浮點數,例如 12.34 能夠轉換爲 1234 來存儲。

日期類型

在 ES 中日期能夠爲如下形式:

  • 格式化的日期字符串,例如 2020-03-17 00:00、2020/03/17
  • 時間戳(和 1970-01-01 00:00:00 UTC 的差值),單位毫秒或者秒

即便是格式化的日期字符串,ES 底層依然採用的是時間戳的形式存儲。

布爾類型

JSON 文檔中一樣存在布爾類型,不過 JSON 字符串類型也能夠被 ES 轉換爲布爾類型存儲,前提是字符串的取值爲 true 或者 false,布爾類型經常使用於檢索中的過濾條件。

二進制類型

二進制類型 binary 接受 BASE64 編碼的字符串,默認 store 屬性爲 false,而且不能夠被搜索。

範圍類型

範圍類型能夠用來表達一個數據的區間,能夠分爲5種:integer_range、float_range、long_range、double_range 以及 date_range

複雜類型

複合類型主要有對象類型(object)和嵌套類型(nested):

對象類型

JSON 字符串容許嵌套對象,一個文檔能夠嵌套多個、多層對象。能夠經過對象類型來存儲二級文檔,不過因爲 Lucene 並無內部對象的概念,ES 會將原 JSON 文檔扁平化,例如文檔:

{
	"name": {
		"first": "wu",
		"last": "px"
	}
}

實際上 ES 會將其轉換爲如下格式,並經過 Lucene 存儲,即便 nameobject 類型:

{
	"name.first": "wu",
	"name.last": "px"
}

嵌套類型

嵌套類型能夠當作是一個特殊的對象類型,可讓對象數組獨立檢索,例如文檔:

{
  "group": "users",
  "username": [
	{ "first": "wu", "last": "px"},
	{ "first": "hu", "last": "xy"},
	{ "first": "wu", "last": "mx"}
  ]
}

username 字段是一個 JSON 數組,而且每一個數組對象都是一個 JSON 對象。若是將 username 設置爲對象類型,那麼 ES 會將其轉換爲:

{
  "group": "users",
  "username.first": ["wu", "hu", "wu"],
  "username.last": ["px", "xy", "mx"]
}

能夠看出轉換後的 JSON 文檔中 firstlast 的關聯丟失了,若是嘗試搜索 firstwulastxy 的文檔,那麼成功會檢索出上述文檔,可是 wuxy 在原 JSON 文檔中並不屬於同一個 JSON 對象,應當是不匹配的,即檢索不出任何結果。

嵌套類型就是爲了解決這種問題的,嵌套類型將數組中的每一個 JSON 對象做爲獨立的隱藏文檔來存儲,每一個嵌套的對象都可以獨立地被搜索,因此上述案例中雖然表面上只有 1 個文檔,但其實是存儲了 4 個文檔。

地理類型

地理類型字段分爲兩種:經緯度類型和地理區域類型:

經緯度類型

經緯度類型字段(geo_point)能夠存儲經緯度相關信息,經過地理類型的字段,能夠用來實現諸如查找在指定地理區域內相關的文檔、根據距離排序、根據地理位置修改評分規則等需求。

地理區域類型

經緯度類型能夠表達一個點,而 geo_shape 類型能夠表達一塊地理區域,區域的形狀能夠是任意多邊形,也能夠是點、線、面、多點、多線、多面等幾何類型。

特殊類型

特殊類型包括 IP 類型、過濾器類型、Join 類型、別名類型等,在這裏簡單介紹下 IP 類型和 Join 類型,其餘特殊類型能夠查看官方文檔。

IP 類型

IP 類型的字段能夠用來存儲 IPv4 或者 IPv6 地址,若是須要存儲 IP 類型的字段,須要手動定義映射:

{
  "mappings": {
	"properties": {
	  "my_ip": {
	    "type": "ip"
	  }
	}
  }
}

Join 類型

Join 類型是 ES 6.x 引入的類型,以取代淘汰的 _parent 元字段,用來實現文檔的一對1、一對多的關係,主要用來作父子查詢。

Join 類型的 Mapping 以下:

PUT my_index
{
  "mappings": {
    "properties": {
      "my_join_field": { 
        "type": "join",
        "relations": {
          "question": "answer" 
        }
      }
    }
  }
}

其中,my_join_field 爲 Join 類型字段的名稱;relations 指定關係:questionanswer 的父類。

例如定義一個 ID 爲 1 的父文檔:

PUT my_join_index/1?refresh
{
  "text": "This is a question",
  "my_join_field": "question" 
}

接下來定義一個子文檔,該文檔指定了父文檔 ID 爲 1:

PUT my_join_index/_doc/2?routing=1&refresh 
{
  "text": "This is an answer",
  "my_join_field": {
    "name": "answer", 
    "parent": "1" 
  }
}

再瞭解完字段數據類型後,再讓咱們看下什麼是 Dynamic Mapping?

什麼是 Dynamic Mapping?

Dynamic Mapping 機制使咱們不須要手動定義 Mapping,ES 會自動根據文檔信息來判斷字段合適的類型,可是有時候也會推算的不對,好比地理位置信息有可能會判斷爲 Text,當類型若是設置不對時,會致使一些功能沒法正常工做,好比 Range 查詢。

類型自動識別

ES 類型的自動識別是基於 JSON 的格式,若是輸入的是 JSON 是字符串且格式爲日期格式,ES 會自動設置成 Date 類型;當輸入的字符串是數字的時候,ES 默認會當成字符串來處理,能夠經過設置來轉換成合適的類型;若是輸入的是 Text 字段的時候,ES 會自動增長 keyword 子字段,還有一些自動識別以下圖所示:

類型自動識別

下面咱們經過一個例子是看看是怎麼類型自動識別的,輸入以下請求,建立索引:

PUT /mapping_test/_doc/1
{
  "uid": "123",
  "username": "wupx",
  "birth": "2020-03-16",
  "married": false,
  "age": 18,
  "heigh": 180,
  "tags": [
    "java",
    "boy"
  ],
  "money": 999.9
}

而後使用 GET /mapping_test/_mapping 查看,結果以下圖所示:

能夠從結果中看出,ES 會根據文檔信息自動推算出合適的類型。

哦豁,萬一我想修改 Mapping 的字段類型,可否更改呢?讓咱們分如下兩種狀況來探究下:

修改 Mapping 字段類型?

若是是新增長的字段,根據 Dynamic 的設置分爲如下三種情況:

  • 當 Dynamic 設置爲 true 時,一旦有新增字段的文檔寫入,Mapping 也同時被更新。
  • 當 Dynamic 設置爲 false 時,索引的 Mapping 是不會被更新的,新增字段的數據沒法被索引,也就是沒法被搜索,可是信息會出如今 _source 中。
  • 當 Dynamic 設置爲 strict 時,文檔寫入會失敗。

另一種是字段已經存在,這種狀況下,ES 是不容許修改字段的類型的,由於 ES 是根據 Lucene 實現的倒排索引,一旦生成後就不容許修改,若是但願改變字段類型,必須使用 Reindex API 重建索引。

不能修改的緣由是若是修改了字段的數據類型,會致使已被索引的沒法被搜索,可是若是是增長新的字段,就不會有這樣的影響。

總結

本文主要介紹了 Mapping 和 Dynamic Mapping,同時對字段類型作了詳細介紹,也介紹了在 ES 中是如何對字段類型作推算的,瞭解了 Mapping 的相關參數設置。

在公衆號【武培軒】回覆【es】獲取思惟導圖以及源代碼。

參考文獻

《Elasticsearch技術解析與實戰》

Elastic Stack從入門到實踐

Elasticsearch核心技術與實戰

https://www.elastic.co/guide/en/elasticsearch/reference/7.1/mapping.html

相關文章
相關標籤/搜索