前言
在關系型數據庫設計當中,表的設計尤其重要,然而關系型數據庫更關注的表與表之間的關系,以及表的划分是否合理,而 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
字段。