概要
本篇接着前一篇內容,繼續介紹mapping信息,重點傾向於自定義mapping、自定義對象以及數組集合類的底層結構。
自定義mapping
上一篇文章介紹的都是Elasticsearch的自動mapping,我們在創建索引時,可以先指定好mapping的信息,還是以music索引爲例:
PUT /music
{
"mappings": {
"children": {
"properties": {
"content": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"language": {
"type": "keyword"
},
"length": {
"type": "long"
},
"likes": {
"type": "long"
},
"name": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
}
}
}
共包含name,content,language,length,likes 5個field,這裏我們把language field的type指定爲keyword,這個field無需分詞,精確匹配即可。
修改mapping
我們可以在創建索引時指定mapping信息,也可以爲索引增加新的field時指定mapping信息,但是已經存在的field,我們不能修改其mapping,否則會報錯,例如:
我們爲music索引增加一個author field,並指定其type爲text,analyzer、search_analyzer均爲english
PUT /music/_mapping/children
{
"properties" : {
"author" : {
"type" : "text",
"index": true,
"analyzer": "english",
"search_analyzer": "english"
}
}
}
如果嘗試修改一下已經存在的field,如name字段,則會提示報錯:
請求:
PUT /music/_mapping/children
{
"properties" : {
"name" : {
"type" : "text",
"index": true,
"analyzer": "english",
"search_analyzer": "english"
}
}
}
報錯信息:
{
"error": {
"root_cause": [
{
"type": "illegal_argument_exception",
"reason": "Mapper for [name] conflicts with existing mapping in other types:\n[mapper [name] has different [analyzer]]"
}
],
"type": "illegal_argument_exception",
"reason": "Mapper for [name] conflicts with existing mapping in other types:\n[mapper [name] has different [analyzer]]"
},
"status": 400
}
複雜數據類型底層結構
前面提及的mapping信息,都是針對基礎數據類型的,我們知道Elasticsearch是面向對象的分佈式存儲文檔系統,肯定會遇到各種各樣的自定義對象、集合數組、嵌套對象等數據結構,ES是如何支持、處理這些複雜對象類型的呢?
- 數組、集合類
集合在JSON串中與數組等同,均可以認爲是數組類型,在Elasticsearch中對數組沒有特殊的映射需求,建立索引時與text是一樣的,也是分詞處理後得到多個詞條,建立倒排索引。
有一點要注意的是數組內的元素,數據類型要一致,比如都是字符器,都是日期,或都是數值,不能混搭,自動mapping是以第1位元素來決定type的,如果混搭,數組內與第1位元素類型不一致的元素,索引時會報錯。
- Null類型
底層的Lucene不能存儲null值,null類型的不會被索引:
"null_value": null,
"empty_array": [],
"array_with_null_value": [ null ]
- 複雜對象
複雜對象一般有一層或多層嵌套,即對象中的屬性,類型也是對象,有時候還混搭着數組一類的,舉個例子:
{
"address": {
"country": "CN",
"province": "GD",
"city": "SZ"
},
"name": "Herry",
"age": 28,
"birth_date": "1992-04-29"
}
人員信息除了姓名年齡外,還有地址信息address,而address屬性本身也是個對象,裏面包含了country、province、city三個屬性,查看它的mapping信息,也是呈嵌套結構(內容有刪除,只保留體現層級的屬性):
{
"person": {
"mappings": {
"info": {
"properties": {
"address": {
"properties": {
"city": {
"type": "text"
},
"country": {
"type": "text"
},
"province": {
"type": "text"
}
}
}
}
}
}
}
}
Elasticsearch在存儲層級結構的數據對象時,會做一些扁平化處理,Luence文檔是一組KV結構的列表,把上述的數據存儲成這樣:
{
"address.country": "CN",
"address.province": "GD",
"address.city": "SZ",
"name": "Herry",
"age": 28,
"birth_date": "1992-04-29"
}
所以最終想要根據address下的country作爲條件進行查詢,應該這麼寫:
GET /person/info/_search
{
"query": {
"match": {
"address.country": "CN"
}
}
}
- 數組內的複雜對象
數組內的元素如果是基礎數據類型還好說,只要求數組內所有元素類型一致即可,如果裏面的元素是一個對象,又是如何索引的呢?
假如一個數組結構是這樣的:
{
"likes": [
{ "name": "Three Zhang", "datetime": "2019-12-01 08:58:12"},
{ "name": "Four Lee", "datetime": "2019-12-01 09:12:23"},
{ "name": "Five Wang", "datetime": "2019-12-01 09:15:58"}
]
}
經過扁平化處理(由列變成行),得到的數據將會是這樣:
{
"likes.name": ["Three Zhang","Four Lee","Five Wang"],
"likes.datetime": ["2019-12-01 08:58:12","2019-12-01 09:12:23","2019-12-01 09:15:58"],
}
發現什麼問題沒?這樣處理完了之後,原本對象之間的關聯性就沒有了,數組內只是一堆無序的元素,如果要查詢"Three Zhang在2019-12-01 09:00:00之後有沒有給我點過贊"這樣的組合條件查詢,是會出現一條查詢記錄的,這跟預期的結果不同,顯然是錯了。
留意一下這個問題,數組內的元素是對象類型時,需要聲明爲nested類型,才能得到預期的查詢效果,nested類型在後續會有介紹。
小結
本篇主要對mapping的內容進行一些補充,並簡單描述了各種複雜對象的底層存儲結構,需要注意的是數組內的對象處理,需要聲明爲nested才能得到正確的結果。
專注Java高併發、分佈式架構,更多技術乾貨分享與心得,請關注公衆號:Java架構社區