Elasticsearch的Mapping,定義了索引的結(jié)構(gòu),類似于關(guān)系型數(shù)據(jù)庫(kù)的Schema。Elasticsearch的Setting主要是為了定義搜索的最關(guān)鍵組件,即:Analyzer,也就是分析器。
Dynamic Mapping及常用字段類型
Mapping的定義
Mapping類似于關(guān)系型數(shù)據(jù)庫(kù)的Schema,主要包含以下內(nèi)容:
- 定義索引中字段的名稱
- 定義字段的數(shù)據(jù)類型,如:字符串、數(shù)字、boolean等
- 可對(duì)字段設(shè)置倒排索引的相關(guān)配置,如是否需要分詞,使用什么分詞器
從7.x開(kāi)始,一個(gè)Mapping只屬于一個(gè)索引的type
- 每個(gè)文檔屬于一個(gè)type
- 一個(gè)type有且僅有一個(gè)Mapping定義
- 從7.x開(kāi)始,不需要在Mapping中指定type信息,默認(rèn)type為
_doc
常用字段類型
在Elasticsearch中,字段數(shù)據(jù)類型有以下常用的類型:
- 簡(jiǎn)單類型
- Text / Keyword - 文本 / 關(guān)鍵字
- Date - 日期
- Integer / Float - 數(shù)字 / 浮點(diǎn)
- Boolean - 布爾值
- IPv4 / IPv6 - ip地址
- 復(fù)雜類型,包括對(duì)象和數(shù)組
- 對(duì)象
- 數(shù)組
- 特殊類型,如地理信息
- geo_point / ...
動(dòng)態(tài)Mapping
動(dòng)態(tài)Mapping,英文名為Dynamic Mapping。
- 在寫(xiě)入文檔時(shí),如果索引不存在,會(huì)自動(dòng)創(chuàng)建索引
- 這種機(jī)制,使得我們無(wú)需手動(dòng)定義mappings。Elasticsearch會(huì)自動(dòng)根據(jù)文檔信息,推算出字段的類型
- 有的時(shí)候,Elasticsearch可能會(huì)推算不對(duì),如:地理位置信息
- 當(dāng)類型推算得不對(duì)時(shí),可能導(dǎo)致一些功能無(wú)法正常運(yùn)行,如Range查詢。
常用類型的自動(dòng)識(shí)別規(guī)則
| 類型 | 規(guī)則 |
|---|---|
| 字符串 | 匹配到日期格式,設(shè)置成Date。 字符串為數(shù)字時(shí),當(dāng)成字符串處理,但我們?cè)O(shè)置轉(zhuǎn)換為數(shù)字。 其他情況,類型就是Text,并且會(huì)增加keyword的子字段 |
| 布爾值 | Boolean |
| 浮點(diǎn)數(shù) | Float |
| 整數(shù) | Long |
| 對(duì)象 | Object |
| 數(shù)組 | 由第一個(gè)非空數(shù)值的類型決定 |
| 空值 | 忽略 |
# 寫(xiě)入文檔,查看 Mapping
PUT mapping_test/_doc/1
{
"firstName": "Chan", -- Text
"lastName": "Jackie", -- Text
"loginDate": "2018-07-24T10:29:48.103Z" -- Date
}
# Dynamic Mapping,推斷字段的類型
PUT mapping_test/_doc/1
{
"uid": "123", -- Text
"isVip": false, -- Boolean
"isAdmin": "true", -- Text
"age": 19, -- Long
"heigh": 180 -- Long
}
# 查看 Dynamic Mapping
GET mapping_test/_mapping
字段類型是否可修改
- 新增加的字段
- dynamic設(shè)為true時(shí),新增字段的文檔寫(xiě)入時(shí),Mapping同時(shí)被更新
- dynamic設(shè)為false時(shí),Mapping不會(huì)被更新,新增字段的數(shù)據(jù)無(wú)法被索引,但是會(huì)出現(xiàn)在_source中
- dynamic設(shè)為strict,文檔將寫(xiě)入失敗
- 已存在的字段,一旦數(shù)據(jù)被寫(xiě)入,就不再支持修改字段定義
- Lucene本身的限制
- 如果希望更改字段類型,必須Reindex api,即:重建索引。在數(shù)據(jù)量多的時(shí)候,開(kāi)銷將非常大
# dynamic設(shè)置為false
PUT idx1
{
"mapping": {
"_doc": {
"dynamic": "false"
}
}
}
# 修改為dynamic為false
PUT idx1/_mapping
{
"dynamic": false
}
# 查看索引
GET idx1/_mapping
dynamic屬性和索引字段可變性的規(guī)則,我們可以總結(jié)如下:
| \ | true | false | strict |
|---|---|---|---|
| 文檔可索引 | yes | yes | no |
| 字段可索引 | yes | no | no |
| Mapping被更新 | yes | no | no |
顯式Mapping及常見(jiàn)參數(shù)
在本文的上一段落,我們的Mapping都是自動(dòng)生成的。自動(dòng)生成機(jī)制雖然方便,但是也可能導(dǎo)致一些問(wèn)題。比如:生成的字段類型不正確,字段的附加屬性不滿足我們的需求,等等。這時(shí),我們可以通過(guò)顯式Mapping的方式來(lái)解決。
那么,我們?nèi)绾芜M(jìn)行顯式Mapping的設(shè)置呢?
- 參考官網(wǎng)api,純手寫(xiě)
- 為減少工作量,減少出錯(cuò)概率,可如下進(jìn)行:
- 創(chuàng)建一個(gè)臨時(shí)index,寫(xiě)入一些樣本數(shù)據(jù)
- 通過(guò)訪問(wèn)Mapping API獲取該臨時(shí)文件的動(dòng)態(tài)Mapping定義
- 修改后,再使用此配置創(chuàng)建自己的索引
- 刪除臨時(shí)索引
我們推薦使用第二種方式,效率高,且不容易出錯(cuò)。
常見(jiàn)參數(shù) - index
index,可用于設(shè)置字段是否被索引,默認(rèn)為true,false即為不可搜索。在下述例子中,mobile字段將不能被搜索到。
# 設(shè)置 index 為 false
DELETE users
PUT users
{
"mappings" : {
"properties" : {
"firstName" : {
"type" : "text"
},
"lastName" : {
"type" : "text"
},
"mobile" : {
"type" : "text",
"index": false
}
}
}
}
常見(jiàn)參數(shù) - index_options
記錄索引級(jí)別。Text類型默認(rèn)為positions,其他類型默認(rèn)為docs。我們需要記住一條準(zhǔn)則。
記錄的內(nèi)容越多,占用的存儲(chǔ)空間就越大。
索引級(jí)別有以下幾種,更細(xì)節(jié)的內(nèi)容可參考【官網(wǎng)索引級(jí)別】
- docs
- freqs
- positions
- offsets
常見(jiàn)參數(shù) - null_value
需要對(duì)Null值實(shí)現(xiàn)搜索時(shí)使用。只有keyword類型才支持設(shè)定null_value。
# 設(shè)定Null_value
DELETE users
PUT users
{
"mappings" : {
"properties" : {
"firstName" : {
"type" : "text"
},
"lastName" : {
"type" : "text"
},
"mobile" : {
"type" : "keyword",
"null_value": "NULL"
}
}
}
}
PUT users/_doc/1
{
"firstName":"Zhang",
"lastName": "Fubing",
"mobile": null
}
PUT users/_doc/2
{
"firstName":"Zhang",
"lastName": "Fubing2"
}
# 查看結(jié)果,有且僅有_id為2的記錄
GET users/_search
{
"query": {
"match": {
"mobile":"NULL"
}
}
}
常見(jiàn)參數(shù) - copy_to
這個(gè)屬性用于將當(dāng)前字段拷貝到指定字段。
-
_all在7.x版本已經(jīng)被copy_to所代替 - 可用于滿足特定場(chǎng)景
-
copy_to將字段數(shù)值拷貝到目標(biāo)字段,實(shí)現(xiàn)類似_all的作用 -
copy_to的目標(biāo)字段不出現(xiàn)在_source中
# 設(shè)置 Copy to
DELETE users
PUT users
{
"mappings": {
"properties": {
"firstName":{
"type": "text",
"copy_to": "fullName"
},
"lastName":{
"type": "text",
"copy_to": "fullName"
}
}
}
}
PUT users/_doc/1
{
"firstName":"Zhang",
"lastName": "Fubing"
}
GET users/_search?q=fullName:(Zhang Fubing)
特殊的數(shù)組類型
Elasticsearch不提供專門的數(shù)組類型。但任何字段,都可以包含多個(gè)相同類型的數(shù)值。
# 數(shù)組類型
PUT users/_doc/1
{
"name":"onebird",
"interests":"reading"
}
PUT users/_doc/1
{
"name":"twobirds",
"interests":["reading","music"]
}
POST users/_search
{
"query": {
"match_all": {}
}
}
# interests字段還是text類型
GET users/_mapping
多字段類型及自定義Analyzer
多字段類型
所謂多字段類型,即:一個(gè)字段可以有多個(gè)子字段。這種特性帶來(lái)了以下好處。
- 增加一個(gè)keyword子字段,可用于精確匹配
- 可對(duì)子字段設(shè)置不同的analyzer
- 不通語(yǔ)言的支持
- 可對(duì)中文拼音字段進(jìn)行搜索
- 可對(duì)搜索和索引指定不同的Analyzer
精確值和全文本
精確值(Exact Values) vs 全文本(Full Text)
- 精確值,包括數(shù)字、日期、具體的字符串(如“192.168.0.1”)
- Elasticsearch中類型為keyword,索引時(shí),不需要做特殊的分詞處理
- 全文本,非結(jié)構(gòu)化的文本數(shù)據(jù)
- Elasticsearch中類型為text,索引時(shí),需要對(duì)其進(jìn)行分詞處理
如下結(jié)構(gòu)的數(shù)據(jù),我們可以大致判斷出哪些是精確值,哪些是全文本。其中的200、info、debug都是精確值。而message的內(nèi)容為全文本。
{
"code": 200,
"message": "this is a error item, you can change your apollo config !",
"content": {
"tags": [
"info",
"debug"
]
}
}
自定義分詞器
自定義分詞器,可通過(guò)組合不同的Character Filter、Tokenizer和Token Filter來(lái)實(shí)現(xiàn)。
Character Filter,常用的字符過(guò)濾器包括:
| 類型 | 作用 |
|---|---|
| html strip | 去除html |
| mapping | 字符串替換 |
| pattern replace | 正則匹配替換 |
Tokenizer,用于將原始文本按照一定規(guī)則切分為詞(Term或Token)。我們除了使用Elasticsearch自動(dòng)的分詞器外,還可以自己通過(guò)開(kāi)發(fā)插件的方式來(lái)實(shí)現(xiàn)。常用的分詞器包括:
- whitespace
- standard
- uax_url_email
- pattern
- keyword
- path
- hierarchy
Token Filter,分詞過(guò)濾器。主要用于對(duì)輸出的單詞,進(jìn)行增刪改。常用的分詞過(guò)濾器包括:
| 類型 | 作用 |
|---|---|
| lowercase | 轉(zhuǎn)換為小寫(xiě) |
| stop | 去掉the、a、an等單詞 |
| synonym | 轉(zhuǎn)換為近義詞 |
在下面的例子中,我們實(shí)現(xiàn)了一個(gè)自定義分詞器。
# 編寫(xiě)自定義分析器
PUT index1
{
"settings": {
"analysis": {
"analyzer": {
"my_custom_analyzer": {
"type": "custom",
"char_filter": ["emoticons"],
"tokenizer": "punctuation",
"filter": [
"lowercase",
"english_stop"
]
}
},
"tokenizer": {
"punctuation": {
"type":"pattern",
"pattern": "[ .,!?]"
}
},
"char_filter": {
"emoticons": {
"type": "mapping",
"mappings": [
":) => _happy_",
":( => _sad_"
]
}
},
"filter": {
"english_stop": {
"type": "stop",
"stopwords": "_english_"
}
}
}
}
}
# 查看自定義分析器的效果
POST index1/_analyze
{
"analyzer": "my_custom_analyzer",
"text": "I'm a :) person, and you?"
}
總結(jié)
通過(guò)這篇文章,我們了解了Mapping的作用及常用字段類型,也知道了動(dòng)態(tài)Mapping和顯式Mapping的區(qū)別。另外,我們還了解了Mapping的多字段特性,以及如何自定義一個(gè)Analyzer。