大概閱讀10分鐘
本教程是系列教程,對(duì)于初學(xué)者可以對(duì) ES 有一個(gè)整體認(rèn)識(shí)和實(shí)踐實(shí)戰(zhàn)。
還沒(méi)開始的同學(xué),建議先讀一下系列攻略目錄:Springboot2.x整合ElasticSearch7.x實(shí)戰(zhàn)目錄
本篇幅是繼上一篇 Springboot2.x整合ElasticSearch7.x實(shí)戰(zhàn)(二) ,適合初學(xué) Elasticsearch 的小白,可以跟著整個(gè)教程做一個(gè)練習(xí)。
[toc]
第五章 Mapping詳解
Mapping 是整個(gè) ES 搜索引擎中最重要的一部分之一,學(xué)會(huì)構(gòu)建一個(gè)好的索引,可以讓我們的搜索引擎更高效,更節(jié)省資源。
什么是 Mapping?
Mapping 是Elasticsearch 中一種術(shù)語(yǔ), Mapping 類似于數(shù)據(jù)庫(kù)中的表結(jié)構(gòu)定義 schema,它有以下幾個(gè)作用:
1. 定義索引中的字段的名稱
2. 定義字段的數(shù)據(jù)類型,比如字符串、數(shù)字、布爾
3. 字段,倒排索引的相關(guān)配置,比如設(shè)置某個(gè)字段為不被索引、記錄 position(位置) 等
在 ES 早期版本,一個(gè)索引下是可以有多個(gè) Type ,從 7.0 開始,一個(gè)索引只有一個(gè) Type,也可以說(shuō)一個(gè) Type 有一個(gè) Mapping 定義。
了解了什么是 Mapping 后,接下來(lái)對(duì) Mapping 的設(shè)置坐下介紹:
Maping設(shè)置
dynamic (動(dòng)態(tài)Mapping)
官網(wǎng)參考:https://www.elastic.co/guide/en/elasticsearch/reference/7.1/mapping.html
PUT users
{
"mappings": {
"_doc": {
"dynamic": false
}
}
}
在創(chuàng)建一個(gè)索引的時(shí)候,可以對(duì) dynamic 進(jìn)行設(shè)置,可以設(shè)成 false、true 或者 strict。

比如一個(gè)新的文檔,這個(gè)文檔包含一個(gè)字段,當(dāng) Dynamic 設(shè)置為 true 時(shí),這個(gè)文檔可以被索引進(jìn) ES,這個(gè)字段也可以被索引,也就是這個(gè)字段可以被搜索,Mapping 也同時(shí)被更新;當(dāng) dynamic 被設(shè)置為 false 時(shí)候,存在新增字段的數(shù)據(jù)寫入,該數(shù)據(jù)可以被索引,但是新增字段被丟棄;當(dāng)設(shè)置成 strict 模式時(shí)候,數(shù)據(jù)寫入直接出錯(cuò)。
index
另外還有 index 參數(shù),用來(lái)控制當(dāng)前字段是否被索引,默認(rèn)為 true,如果設(shè)為 false(有些業(yè)務(wù)場(chǎng)景,某些字段不希望被搜索到),則該字段不可被搜索。
# index屬性控制 字段是否可以被索引
PUT user_test
{
"mappings": {
"properties": {
"firstName":{
"type": "text"
},
"lastName":{
"type": "text"
},
"mobile" :{
"type": "text",
"index": false
}
}
}
}
index_options

參數(shù) index_options 用于控制倒排索引記錄的內(nèi)容,有如下 4 種配置:
- doc:只記錄 doc id
- freqs:記錄 doc id 和 term frequencies
- positions:記錄 doc id、term frequencies 和 term position
- offsets:記錄 doc id、term frequencies、term position 和 character offects
另外,text 類型默認(rèn)配置為 positions,其他類型默認(rèn)為 doc,記錄內(nèi)容越多,占用存儲(chǔ)空間越大。
null_value
null_value 主要是當(dāng)字段遇到 null 值時(shí)的處理策略,默認(rèn)為 NULL,即空值,此時(shí) ES 會(huì)默認(rèn)忽略該值,可以通過(guò)設(shè)定該值設(shè)定字段的默認(rèn)值,另外只有 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"
}
}
}
_all
這個(gè)屬性現(xiàn)在使用很少,不做深入講解
參考官網(wǎng):https://www.elastic.co/guide/cn/elasticsearch/guide/current/root-object.html
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中
DELETE users
PUT users
{
"mappings": {
"properties": {
"firstName":{
"type": "text",
"copy_to": "fullName"
},
"lastName":{
"type": "text",
"copy_to": "fullName"
}
}
}
}
PUT users/_doc/1
{
"firstName":"Li",
"lastName": "Sunke"
}
//沒(méi)有新建字段
GET users/_doc/1
{
"_index" : "users",
"_type" : "_doc",
"_id" : "1",
"_version" : 1,
"_seq_no" : 0,
"_primary_term" : 1,
"found" : true,
"_source" : {
"firstName" : "Li",
"lastName" : "Sunke"
}
}
GET users/_search?q=fullName:(Li sunke)
以前的用法是:
curl -XPUT 'localhost:9200/my_index?pretty' -H 'Content-Type: application/json' -d'
{
"mappings": {
"my_type": {
"properties": {
"first_name": {
"type": "text",
"copy_to": "full_name" # 1
},
"last_name": {
"type": "text",
"copy_to": "full_name" # 2
},
"full_name": {
"type": "text"
}
}
}
}
}
'
curl -XPUT 'localhost:9200/my_index/my_type/1?pretty' -H 'Content-Type: application/json' -d'
{
"first_name": "John",
"last_name": "Smith"
}
'
curl -XGET 'localhost:9200/my_index/_search?pretty' -H 'Content-Type: application/json' -d'
{
"query": {
"match": {
"full_name": { # 3
"query": "John Smith",
"operator": "and"
}
}
}
}
'
- first_name(名字)和 last_name(姓氏)字段復(fù)制到full_name 字段;
- first_name(名字)和 last_name(姓氏)字段仍然可以分別查詢;
- full_name 可以通過(guò) first_name(名字)和 last_name(姓氏)來(lái)查詢;
一些要點(diǎn):
- 復(fù)制的是字段值,而不是 term(詞條)(由分析過(guò)程產(chǎn)生).
- _source 字段不會(huì)被修改來(lái)顯示復(fù)制的值.
- 相同的值可以復(fù)制到多個(gè)字段,通過(guò) "copy_to": [ "field_1", "field_2" ] 來(lái)操作.
分詞器analyzer和arch_analyzer
PUT /my_index
{
"mappings": {
"properties": {
"text": {
"type": "text",
"fields": {
"english": {
"type": "text",
"analyzer": "english",
"search_analyzer": "english"
}
}
}
}
}
}
#使用_analyze 測(cè)試分詞器
GET my_index/_analyze
{
"field": "text",
"text": "The quick Brown Foxes."
}
GET my_index/_analyze
{
"field": "text.english",
"text": "The quick Brown Foxes."
}
構(gòu)建Mapping方式
我們知道 Mapping 是可以通過(guò)我們插入的文檔自動(dòng)生成索引,但是可能還是有一些問(wèn)題。例如:生成的字段類型不正確,字段的附加屬性不滿足我們的需求。這是我們可以通過(guò)顯式Mapping的方式來(lái)解決。倆種方法:
- 參考官網(wǎng)api,純手寫
- 構(gòu)建臨時(shí)索引;寫入一些樣本數(shù)據(jù);通過(guò)Maping API 查詢臨時(shí)文件的動(dòng)態(tài)Mapping 定義;修改后、再使用此配置創(chuàng)建索引;刪除臨時(shí)索引;
推薦第二種,不容易出錯(cuò),效率高。
類型自動(dòng)識(shí)別
ES 類型的自動(dòng)識(shí)別是基于 JSON 的格式,如果輸入的是 JSON 是字符串且格式為日期格式,ES 會(huì)自動(dòng)設(shè)置成 Date 類型;當(dāng)輸入的字符串是數(shù)字的時(shí)候,ES 默認(rèn)會(huì)當(dāng)成字符串來(lái)處理,可以通過(guò)設(shè)置來(lái)轉(zhuǎn)換成合適的類型;如果輸入的是 Text 字段的時(shí)候,ES 會(huì)自動(dòng)增加 keyword 子字段,還有一些自動(dòng)識(shí)別如下圖所示:

- Demo:
# 寫入文檔,查看 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
映射參數(shù)

mappings 中field定義選擇:
"field": {
"type": "text", //文本類型
"index": "false"http:// ,設(shè)置成false,字段將不會(huì)被索引
"analyzer":"ik"http://指定分詞器
"boost":1.23//字段級(jí)別的分?jǐn)?shù)加權(quán)
"doc_values":false//對(duì)not_analyzed字段,默認(rèn)都是開啟,analyzed字段不能使用,對(duì)排序和聚合能提升較大性能,節(jié)約內(nèi)存,如果您確定不需要對(duì)字段進(jìn)行排序或聚合,或者從script訪問(wèn)字段值,則可以禁用doc值以節(jié)省磁盤空間:
"fielddata":{"loading" : "eager" }//Elasticsearch 加載內(nèi)存 fielddata 的默認(rèn)行為是 延遲 加載 。 當(dāng) Elasticsearch 第一次查詢某個(gè)字段時(shí),它將會(huì)完整加載這個(gè)字段所有 Segment 中的倒排索引到內(nèi)存中,以便于以后的查詢能夠獲取更好的性能。
"fields":{"keyword": {"type": "keyword","ignore_above": 256}} //可以對(duì)一個(gè)字段提供多種索引模式,同一個(gè)字段的值,一個(gè)分詞,一個(gè)不分詞
"ignore_above":100 //超過(guò)100個(gè)字符的文本,將會(huì)被忽略,不被索引
"include_in_all":ture//設(shè)置是否此字段包含在_all字段中,默認(rèn)是true,除非index設(shè)置成no選項(xiàng)
"index_options":"docs"http://4個(gè)可選參數(shù)docs(索引文檔號(hào)) ,freqs(文檔號(hào)+詞頻),positions(文檔號(hào)+詞頻+位置,通常用來(lái)距離查詢),offsets(文檔號(hào)+詞頻+位置+偏移量,通常被使用在高亮字段)分詞字段默認(rèn)是position,其他的默認(rèn)是docs
"norms":{"enable":true,"loading":"lazy"}//分詞字段默認(rèn)配置,不分詞字段:默認(rèn){"enable":false},存儲(chǔ)長(zhǎng)度因子和索引時(shí)boost,建議對(duì)需要參與評(píng)分字段使用 ,會(huì)額外增加內(nèi)存消耗量
"null_value":"NULL"http://設(shè)置一些缺失字段的初始化值,只有string可以使用,分詞字段的null值也會(huì)被分詞
"position_increament_gap":0//影響距離查詢或近似查詢,可以設(shè)置在多值字段的數(shù)據(jù)上火分詞字段上,查詢時(shí)可指定slop間隔,默認(rèn)值是100
"store":false//是否單獨(dú)設(shè)置此字段的是否存儲(chǔ)而從_source字段中分離,默認(rèn)是false,只能搜索,不能獲取值
"search_analyzer":"ik"http://設(shè)置搜索時(shí)的分詞器,默認(rèn)跟ananlyzer是一致的,比如index時(shí)用standard+ngram,搜索時(shí)用standard用來(lái)完成自動(dòng)提示功能
"similarity":"BM25"http://默認(rèn)是TF/IDF算法,指定一個(gè)字段評(píng)分策略,僅僅對(duì)字符串型和分詞類型有效
"term_vector":"no"http://默認(rèn)不存儲(chǔ)向量信息,支持參數(shù)yes(term存儲(chǔ)),with_positions(term+位置),with_offsets(term+偏移量),with_positions_offsets(term+位置+偏移量) 對(duì)快速高亮fast vector highlighter能提升性能,但開啟又會(huì)加大索引體積,不適合大數(shù)據(jù)量用
}
總結(jié)一下:
- 與域數(shù)據(jù)格式及約束相關(guān)的參數(shù):normalizer,format,ignore_above,ignore_malformed,coerce
- 與索引相關(guān)的參數(shù):index,dynamic,enabled
- 與存儲(chǔ)策略相關(guān)的參數(shù):store, fielddata,doc_values
- 分析器相關(guān)參數(shù):analyzer,search_analyzer
- 其它參數(shù):boost,copy_to,null_value
對(duì)于這些參數(shù)的描述主要基于筆者的理解,可能有不準(zhǔn)確之處。實(shí)際上這些參數(shù)與ES的實(shí)現(xiàn)機(jī)制(如存儲(chǔ)結(jié)構(gòu),索引結(jié)構(gòu)密切有關(guān)),只能在實(shí)際應(yīng)用中去慢慢體會(huì)。
字段數(shù)據(jù)類型
ES 字段類型類似于 MySQL 中的字段類型,ES 字段類型主要有:核心類型、復(fù)雜類型、地理類型以及特殊類型,具體的數(shù)據(jù)類型如下圖所示:

核心類型
從圖中可以看出核心類型可以劃分為字符串類型、數(shù)字類型、日期類型、布爾類型、基于 BASE64 的二進(jìn)制類型、范圍類型。
字符串類型
其中,在 ES 7.x 有兩種字符串類型:text 和 keyword,在 ES 5.x 之后 string 類型已經(jīng)不再支持了。
text 類型適用于需要被全文檢索的字段,例如新聞?wù)?、郵件內(nèi)容等比較長(zhǎng)的文字,text 類型會(huì)被 Lucene 分詞器(Analyzer)處理為一個(gè)個(gè)詞項(xiàng),并使用 Lucene 倒排索引存儲(chǔ),text 字段不能被用于排序,如果需要使用該類型的字段只需要在定義映射時(shí)指定 JSON 中對(duì)應(yīng)字段的 type 為 text。
keyword 適合簡(jiǎn)短、結(jié)構(gòu)化字符串,例如主機(jī)名、姓名、商品名稱等,可以用于過(guò)濾、排序、聚合檢索,也可以用于精確查詢。
數(shù)字類型
數(shù)字類型分為 long、integer、short、byte、double、float、half_float、scaled_float。
數(shù)字類型的字段在滿足需求的前提下應(yīng)當(dāng)盡量選擇范圍較小的數(shù)據(jù)類型,字段長(zhǎng)度越短,搜索效率越高,對(duì)于浮點(diǎn)數(shù),可以優(yōu)先考慮使用 scaled_float 類型,該類型可以通過(guò)縮放因子來(lái)精確浮點(diǎn)數(shù),例如 12.34 可以轉(zhuǎn)換為 1234 來(lái)存儲(chǔ)。
日期類型
在 ES 中日期可以為以下形式:
格式化的日期字符串,例如 2020-03-17 00:00、2020/03/17
時(shí)間戳(和 1970-01-01 00:00:00 UTC 的差值),單位毫秒或者秒
即使是格式化的日期字符串,ES 底層依然采用的是時(shí)間戳的形式存儲(chǔ)。
布爾類型
JSON 文檔中同樣存在布爾類型,不過(guò) JSON 字符串類型也可以被 ES 轉(zhuǎn)換為布爾類型存儲(chǔ),前提是字符串的取值為 true 或者 false,布爾類型常用于檢索中的過(guò)濾條件。
二進(jìn)制類型
二進(jìn)制類型 binary 接受 BASE64 編碼的字符串,默認(rèn) store 屬性為 false,并且不可以被搜索。
范圍類型
范圍類型可以用來(lái)表達(dá)一個(gè)數(shù)據(jù)的區(qū)間,可以分為5種:integer_range、float_range、long_range、double_range 以及 date_range。
復(fù)雜類型
復(fù)合類型主要有對(duì)象類型(object)和嵌套類型(nested):
對(duì)象類型
JSON 字符串允許嵌套對(duì)象,一個(gè)文檔可以嵌套多個(gè)、多層對(duì)象。可以通過(guò)對(duì)象類型來(lái)存儲(chǔ)二級(jí)文檔,不過(guò)由于 Lucene 并沒(méi)有內(nèi)部對(duì)象的概念,ES 會(huì)將原 JSON 文檔扁平化,例如文檔:
{
"name": {
"first": "wu",
"last": "px"
}
}
實(shí)際上 ES 會(huì)將其轉(zhuǎn)換為以下格式,并通過(guò) Lucene 存儲(chǔ),即使 name 是 object 類型:
{
"name.first": "wu",
"name.last": "px"
}
嵌套類型
嵌套類型可以看成是一個(gè)特殊的對(duì)象類型,可以讓對(duì)象數(shù)組獨(dú)立檢索,例如文檔:
{
"group": "users",
"username": [
{ "first": "wu", "last": "px"},
{ "first": "hu", "last": "xy"},
{ "first": "wu", "last": "mx"}
]
}
username 字段是一個(gè) JSON 數(shù)組,并且每個(gè)數(shù)組對(duì)象都是一個(gè) JSON 對(duì)象。如果將 username 設(shè)置為對(duì)象類型,那么 ES 會(huì)將其轉(zhuǎn)換為:
{
"group": "users",
"username.first": ["wu", "hu", "wu"],
"username.last": ["px", "xy", "mx"]
}
可以看出轉(zhuǎn)換后的 JSON 文檔中 first 和 last 的關(guān)聯(lián)丟失了,如果嘗試搜索 first 為 wu,last 為 xy 的文檔,那么成功會(huì)檢索出上述文檔,但是 wu 和 xy 在原 JSON 文檔中并不屬于同一個(gè) JSON 對(duì)象,應(yīng)當(dāng)是不匹配的,即檢索不出任何結(jié)果。
嵌套類型就是為了解決這種問(wèn)題的,嵌套類型將數(shù)組中的每個(gè) JSON 對(duì)象作為獨(dú)立的隱藏文檔來(lái)存儲(chǔ),每個(gè)嵌套的對(duì)象都能夠獨(dú)立地被搜索,所以上述案例中雖然表面上只有 1 個(gè)文檔,但實(shí)際上是存儲(chǔ)了 4 個(gè)文檔。
地理類型
地理類型字段分為兩種:經(jīng)緯度類型和地理區(qū)域類型:
經(jīng)緯度類型
經(jīng)緯度類型字段(geo_point)可以存儲(chǔ)經(jīng)緯度相關(guān)信息,通過(guò)地理類型的字段,可以用來(lái)實(shí)現(xiàn)諸如查找在指定地理區(qū)域內(nèi)相關(guān)的文檔、根據(jù)距離排序、根據(jù)地理位置修改評(píng)分規(guī)則等需求。
地理區(qū)域類型
經(jīng)緯度類型可以表達(dá)一個(gè)點(diǎn),而 geo_shape 類型可以表達(dá)一塊地理區(qū)域,區(qū)域的形狀可以是任意多邊形,也可以是點(diǎn)、線、面、多點(diǎn)、多線、多面等幾何類型。
特殊類型
特殊類型包括 IP 類型、過(guò)濾器類型、Join 類型、別名類型等,在這里簡(jiǎn)單介紹下 IP 類型和 Join 類型,其他特殊類型可以查看官方文檔。
IP 類型
IP 類型的字段可以用來(lái)存儲(chǔ) IPv4 或者 IPv6 地址,如果需要存儲(chǔ) IP 類型的字段,需要手動(dòng)定義映射:
{
"mappings": {
"properties": {
"my_ip": {
"type": "ip"
}
}
}
}
Join類型
Join 類型是 ES 6.x 引入的類型,以取代淘汰的 _parent 元字段,用來(lái)實(shí)現(xiàn)文檔的一對(duì)一、一對(duì)多的關(guān)系,主要用來(lái)做父子查詢。
Join 類型的 Mapping 如下:
PUT my_index
{
"mappings": {
"properties": {
"my_join_field": {
"type": "join",
"relations": {
"question": "answer"
}
}
}
}
}
其中,my_join_field 為 Join 類型字段的名稱;relations 指定關(guān)系:question 是 answer 的父類。
例如定義一個(gè) ID 為 1 的父文檔:
PUT my_join_index/1?refresh
{
"text": "This is a question",
"my_join_field": "question"
}
接下來(lái)定義一個(gè)子文檔,該文檔指定了父文檔 ID 為 1:
PUT my_join_index/_doc/2?routing=1&refresh
{
"text": "This is an answer",
"my_join_field": {
"name": "answer",
"parent": "1"
}
}
join參考:https://www.elastic.co/guide/en/elasticsearch/reference/current/parent-join.html