前言
在關係型資料庫設計當中,表的設計尤其重要,然而關係型資料庫更關注的表與表之間的關係,以及表的劃分是否合理,而 Elasticsearch
中卻更加關注欄位型別的設計,一個好的欄位型別設計可以更好的利用 Elasticsearch
的搜尋分析特性。
mapping
如果說我們想要用好 Elasticsearch
,那麼就必須要先了解 mapping
什麼是 mapping
。一句話:mapping
是定義如何儲存和索引文件及其包含的欄位的過程。
mapping 能做什麼
前面我們提到,在 Elasticsearch
中,mapping
類似於傳統關係型資料庫的表結構定義,主要做以下幾件事:
- 定義欄位名稱和欄位型別。
- 定義倒排索引相關的配置,比如是否被索引,是否可以被分詞等。
mapping
可以分為兩種:Dynamic mapping
和 Explicit mapping
。
Dynamic mapping
Dynamic mapping
即:動態對映。動態對映顧名思義就是 mapping
會被動態建立,也就是說我們不需要定義 mapping
就可以往一個索引插入資料,插入索引資料之後,Elasticsearch
會根據插入的資料自動推測資料型別,進而動建立 mapping
。
比如下面就是往一個不存在的索引 index_001
插入一條資料:
PUT index_001/_doc/1
{
"name":"lonely wolf",
"age": 18,
"create_date":"2021-05-19 20:45:11",
"update_date":"2021-05-23"
}
插入資料之後,執行 GET index_001
來查詢一下索引資訊:
可以發現,這時候索引已經被自動建立了,而且 age
欄位被 Elasticsearch
定義為 long
型別,update_date
被定義為 data
型別,其他兩個欄位則被推測為 text
型別。
Elasticsearch
中自動對映型別規則可以通過 dynamic
引數進行配置,dynamic
型別有 4
種:
dynamic=true
預設值。當設定為 true
時,一旦有新欄位插入文件,則 mapping
會被同步更新。
我們在上面的文件中再插入一個新文件,新文件新增一個 address
欄位:
PUT index_001/_doc/2
{
"name":"lonely wolf2",
"age": 20,
"create_date":"2021-05-23 11:37:11",
"update_date":"2021-05-23",
"address":"廣東深圳"
}
然後再檢視一下 mapping
,可以看到 mapping
已經新增了一個 address
欄位,mapping
欄位被更新意味著該欄位會加入索引:
dynamic=runtime
這個型別和 true
型別非常相似,但是有一個非常大的區別就是,雖然加入新欄位也會更新 mapping
,但是新加入的欄位不會被索引,也就是不會使得索引變大,不過雖然不被索引,但是新加入的欄位依然可以被查詢,只是查詢的代價會更大。所以這種型別一般不建議用在經常查詢的條件欄位上,而更適合用在一些不確定資料結構的日誌類索引中。
修改 dynamic
型別:
PUT index_001/_mapping
{
"dynamic": "runtime"
}
新增一個文件,並加入一個新欄位:
PUT index_001/_doc/3
{
"email":"123@qq.com"
}
最後詢一下 mapping
,可以看到欄位屬性是 runtime
,而且型別是 keyword
:
下表就是自動建立 mapping
時,Elasticsearch
的對映關係:
插入資料型別 | dynamic=true | dynamic=runtime |
---|---|---|
null | 不會新增任何欄位 | 不會新增任何欄位 |
true 或 false | boolean | boolean |
double | float | double |
integer | long | long |
object | object | object |
string(通過 date 校驗) | date | date |
string(通過 numreic 校驗) | float 或 long | double 或 long |
string(沒有通過 date 或 numreic 校驗) | text ,並且同時會建立一個 keyword 子域 | keyword |
array | 取決於陣列中第一個非 null 值 | 取決於陣列中第一個非 null 值 |
PS:keyword
表示 不參與分詞。
dynamic=false
當設定為 false
時,新加入的欄位不會被更新到 mapping
,也就是說新欄位不會被索引,故以這個欄位為條件進行搜尋時,無法被搜尋到(這一點要注意和 runtime
型別進行區分),不過雖然無法被索引,但是該欄位會出現在 _source
中。也就是說該欄位不能作為查詢條件,但是能被查詢出來。
接下來我們將 dynamic
修改為 false
,並新增一個欄位來驗證,可以發現新增的欄位會出現在 _source
中,但是無法作為條件被查詢出來:
dynamic=strict
這種型別最為嚴格,表示不允許新增一個不在 mapping
中的欄位,一旦新增的欄位不在 mapping
定義中,則直接報錯:
是否可以修改 mapping 中的資料型別
在 Elasticsearch
中,一旦一個欄位被定義在了 mapping
中,是無法被修改的,因為一旦欄位被修改了,就會無法被索引(新增欄位除外),所以一般我們需要修改索引的話,都會重建索引,並採用 reindex
操作來遷移資料。
關閉 dynamic mapping
可以通過以下兩個配置來關閉 dynamic mapping
,以下兩個屬性預設值均為 true
,如果需要關閉,則需要修改為 false
:
action.auto_create_index: true
index.mapper.dynamic: true
Explicit mapping
Explicit mapping
即:顯式對映。也就是說這時候我們需要顯示的定義欄位型別。
Elasticsearch
中支援的欄位型別很多,在這裡就舉一些比較常用的欄位型別:
text 型別
這是最常用的一種型別,儲存字串,用於全文索引。當欄位被定義為 text
型別時,預設不能用於聚合,排序等操作:
可以看到,用 text
型別欄位排序彙報湊,如果想要允許這些操作,可以通過設定 fielddata=true
,如下
PUT my-index-011/_mapping
{
"properties": {
"my_field": {
"type": "text",
"fielddata": true
}
}
}
field
欄位儲存在堆記憶體中,因為其涉及到的計算比較消耗效能,所以一般不建議設定 fielddata=true
,而是通過建立一個 keyword
子域來實現(預設方式):
PUT index_111
{
"mappings": {
"properties": {
"my_field": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword"
}
}
}
}
}
}
這種定義方式我們可以將一個欄位同時作為 text
和 keyword
型別使用,如果要用於聚合或者排序等操作則可以使用 欄位名.keyword
來作為欄位名來進行操作:
keyword 型別
這種型別也非常常用,該欄位儲存的資料表示一個整體,不可被分詞,所以一般不會用來定義大本文的全文檢索欄位,而是用來儲存一些結構化的字串,比如:id,郵箱,標籤等。
keyword
型別一般用於聚合,排序等操作。除此之外,該欄位還有兩種衍生型別:constant_keyword
和 wildcard
。
constant_keyword
:一般用於定義常量型別,比如一個索引中某一個欄位全部為同一個值,可以定義為這種型別。wildcard
:一般用於模糊匹配查詢或者正則匹配查詢。
如下就是一個模糊匹配查詢的示例(可以配合萬用字元使用,類似於關係型資料庫的 like
操作):
GET index_112/_search
{
"query": {
"wildcard": {
"my_wildcard": {
"value": "*quite*lengthy"
}
}
}
}
date 型別
用於定義日期型別,定義日期型別的同時,可以通過 format
來指定日期的格式:
PUT index_113
{
"mappings": {
"properties": {
"date": {
"type": "date",
"format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
}
}
}
}
numeric 型別
Elasticsearch
中提供了比較多的格式用來表示不同長度的數字型別:
數字型別 | 長度 |
---|---|
long | 64 位有符號整數。範圍:-2 的 63 次方到 2 的 63 次方 -1 |
integer | 32 位有符號整數。範圍:-2 的 31 次方到 2 的 31 次方 -1 |
short | 16 位有符號整數。範圍:-32768 到 32767 |
byte | 8 位有符號整數。範圍:-128 到 127 |
double | 64 位雙精度小數 |
float | 32 位單精度小數 |
half_float | 16 位單精度小數 |
scaled_float | 帶有縮放因子的浮點數,一般適用於存放金額之類的資料。比如 18.88 元,縮放因子是 100,那麼在索引時會被索引為 1888(即:原值 * 縮放因子) |
unsigned_long | 64 位無符號整數。範圍:0 到 2 的 64 次方減 1 |
定義方式如下所示:
PUT index_002
{
"mappings": {
"properties": {
"number_of_bytes": {
"type": "integer"
},
"time_in_seconds": {
"type": "float"
},
"price": {
"type": "scaled_float",
"scaling_factor": 100
}
}
}
}
boolean 型別
布林型別比較簡單,只有 true
和 false
兩種:
PUT index_001
{
"mappings": {
"properties": {
"is_published": {
"type": "boolean"
}
}
}
}
其他型別
除了上面介紹的一些比較常用的資料型別,Elasticsearch
中還有一些高階資料型別:如 Nested(巢狀型別),地理資料型別,ip
型別等。
總結
Elasticsearch
中支援動態 mapping
和顯示 mapping
兩種,在使用中有時候可以先插入一條資料到臨時索引,等自動生成 mapping
之後,在對現有 mapping
進行修改調整,在欄位上尤其要考慮好 text
型別和 keyword
型別的設定,如果需要支援全文搜尋和分詞搜尋,則需要使用 text
型別,需要支援關鍵字模糊搜尋或者聚合排序等操作可以考慮使用 keyword
欄位。