申明:此文章是早期學習的時候從其他博主的博文中整理而來的,但不記得是哪位博主的了,無法提供原文鏈接,見諒!!!
什么是 Mapping?
Mapping類似於關系型數據庫的Schema,主要包含以下內容:
- 定義索引中字段的名稱
- 定義字段的數據類型,如:字符串、數字、boolean等
- 可對字段設置倒排索引的相關配置,如是否需要分詞,使用什么分詞器
從7.x開始,一個Mapping只屬於一個索引的type 默認type 為:_doc
- 每個文檔屬於一個type
- 一個type有且僅有一個Mapping定義
- 從7.x開始,不需要在Mapping中指定type信息,默認type為 _doc
- Mapping的數據類型
ES 字段類型類似於 MySQL 中的字段類型,ES 字段類型主要有:核心類型、復雜類型、地理類型以及特殊類型,具體的數據類型如下圖所示
下圖鏈接: https://www.processon.com/view/link/611b33da7d9c08015c29dc61
字符串類型
在 ES 7.x 有兩種字符串類型:text 和 keyword,在 ES 5.x 之后 string 類型已經不再支持了。
text :類型適用於需要被全文檢索的字段,例如新聞正文、郵件內容等比較長的文字,text 類型會被 Lucene 分詞器(Analyzer)處理為一個個詞項,
並使用 Lucene 倒排索引存儲,text 字段不能被用於排序,如果需要使用該類型的字段只需要在定義映射時指定 JSON 中對應字段的 type 為 text。
keyword:不會被分詞,適合簡短、結構化字符串,例如主機名、姓名、商品名稱等,可以用於過濾、排序、聚合檢索,也可以用於精確查詢。
數字類型
數字類型分為 long、integer、short、byte、double、float、half_float、scaled_float。
數字類型的字段在滿足需求的前提下應當盡量選擇范圍較小的數據類型,字段長度越短,搜索效率越高,對於浮點數,可以優先考慮使用 scaled_float 類型,
該類型可以通過縮放因子來精確浮點數,例如 12.34 可以轉換為 1234 來存儲。
日期類型
在 ES 中日期可以為以下形式:https://www.elastic.co/guide/en/elasticsearch/reference/6.4/mapping-date-format.html
格式化的日期字符串,例如 2020-03-17 00:00、2020/03/17時間戳(和 1970-01-01 00:00:00 UTC 的差值),單位毫秒或者秒即使是格式化的日期字符串
ES 底層依然采用的是時間戳的形式存儲。(可同時定義多種格式)
{ "mappings":{ "_doc":{ "properties":{ "create_date":{ "type":"date", "format":"yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis||epoch_second" } } } } }
布爾類型
JSON 文檔中同樣存在布爾類型,不過 JSON 字符串類型也可以被 ES 轉換為布爾類型存儲,前提是字符串的取值為 true 或者 false,布爾類型常用於檢索中的過濾條件。二
進制類型
二進制類型 binary 接受 BASE64 編碼的字符串,默認 store 屬性為 false,並且不可以被搜索。
范圍類型
范圍類型可以用來表達一個數據的區間,可以分為5種:
integer_range、float_range、long_range、double_range 以及 date_range。
創建索引
PUT /range_test { "mappings":{ "_doc":{ "properties":{ "count":{ "type":"integer_range" }, "create_date":{ "type":"date_range", "format":"yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis" } } } } }
添加數據
POST /range_test/_doc/1 { "count":{ "gte":1, "lte":100 }, "create_date":{ "gte":"2019-02-1 12:00:00", "lte":"2019-03-30" } }
檢索其中5 在 1-100之間可以被檢索出來
GET /range_test/_doc/_search { "query":{ "term":{ "count":5 } } }
復雜類型
復合類型主要有對象類型(object)和嵌套類型(nested)
對象類型(object)
JSON 字符串允許嵌套對象,一個文檔可以嵌套多個、多層對象。可以通過對象類型來存儲二級文檔,不過由於 Lucene 並沒有內部對象的概念,
ES 會將原 JSON 文檔扁平化,例如文檔
PUT my_index/_doc/1 { "region":"US", "manager":{ "age":30, "name":{ "first":"John", "last":"Smith" } } }
PUT my_index { "mappings":{ "_doc":{ "properties":{ "region":{ "type":"keyword" }, "manager":{ "properties":{ "age":{ "type":"integer" }, "name":{ "properties":{ "first":{ "type":"text" }, "last":{ "type":"text" } } } } } } } } }
其中域manager就是一個對象類型,其中的name是它的子對象。對於對象類型,缺省設置“type”為”object”,因此不用顯式定義“type”。
對於上面的對象類型,ES在索引時將其轉換為"manager.age", "manager.name.first" 這樣扁平的key,因此查詢時也可以使用這樣的扁平key作為域來進行查詢。
嵌套類型(nested)
嵌套類型可以看成是一個特殊的對象類型,可以讓對象數組獨立檢索,例如文檔
"user" :[ { "first":"John", "last":"Smith" }, { "first":"Alice", "last":"White" } ]
如果使用動態映射,會被ES索引為如下形式
"user.first" :
[
"alice",
"john"
]
"user.last" :
[
"smith",
"white"
]
這樣的索引形式在查詢時會丟失對象中”first”與“last”之間的關聯關系。如果將user映射為如下形式
"user":{ "type":"nested" }
ES在索引時會保留對象域之間的關聯關系,在查詢時找對正確的對象。
如使用如下查詢則找不到任何命中對象(不存在“Alice Smith”這個對象)
{ "query":{ "nested":{ "path":"user", "query":{ "bool":{ "must":[ { "match":{ "user.first":"Alice" } }, { "match":{ "user.last":"Smith" } } ] } } } } }
地理數據類型
地理數據類型可用於LBS的應用,包括:
geo_point類型:可用於存儲某個地理坐標的經緯度。示例如下:(對象方式存儲坐標請使用:lat 、lon 字段,否則ES 不認)
"location":{ "lat":41.12, "lon":-71.34 }
geo_shape類型:用於存儲地理多邊形形狀
特殊數據類型
特殊數據類型包括:
- ip類型:用於表示IPv4與IPv6的地址
- completion類型:提供自動輸入關聯完成功能,如常見的baidu搜索框。
- token_count類型:用於計算字符串token的長度,使用時需提供"analyzer"定義。
- percolate類型:定義為percolate類型的字段會被ES分析為一個查詢並保存下來,並可用在后繼對文檔的查詢中。Percolate可以理解為一個預置的查詢。
- alias類型:定義一個已存在域的別名。
- join類型:該類型定義了文檔對象之間的父子關系(可定義多層,形成一顆層次樹),即同一索引中多個文檔對象可以存在依賴關系,如互聯網應用常見的博客文章與回復,
問題與回答之間的關系。參考官方文檔
定義映射字段:
{ "my_join_field":{ "type":"join", "relations":{ "question":"answer" } } }
my_join_field定義了"question"與"answer"之間關系為父子關系。
觀察對於該映射的一個文檔實例,路徑為“my_index/_doc/1”
{ "text":"This is a question", "my_join_field":"question" }
該文檔的一個子文檔對象示例如下,在my_join_field需要定義父親的ID(這里根據上面的父實例,為1)
{ "text":"This is an answer", "my_join_field":{ "name":"answer", "parent":"1" } }
需要注意的是,一個父文檔可以有多個子文檔,父子文檔應部署在同一個分片上。因而在向ES提交父子文檔時,應在URI中使用相同的routing參數。
join類型定義了文檔之間的父子依賴關系,在查詢和聚合操作中可使用這種依賴關系。
數組類型
Elasticsearch不提供專門的數組類型。但任何字段,都可以包含多個相同類型的數值。
# 數組類型
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
Dynamic Mapping 翻譯為動態Mapping:
- 在寫入文檔時,如果索引不存在,會自動創建索引
- 這種機制,使得我們無需手動定義mappings。Elasticsearch會自動根據文檔信息,推算出字段的類型
- 有的時候,Elasticsearch可能會推算不對,如:地理位置信息
- 當類型推算得不對時,可能導致一些功能無法正常運行,如Range查詢。
類型自動識別
ES 類型的自動識別是基於 JSON 的格式,如果輸入的是 JSON 是字符串且格式為日期格式,ES 會自動設置成 Date 類型;
當輸入的字符串是數字的時候,ES 默認會當成字符串來處理,可以通過設置來轉換成合適的類型;
如果輸入的是 Text 字段的時候,ES 會自動增加 keyword 子字段,還有一些自動識別如下圖所示
類型 |
規則 |
字符串 |
匹配到日期格式,設置成Date。 字符串為數字時,當成字符串處理,但我們設置轉換為數字。 其他情況,類型就是Text,並且會增加keyword的子字段 |
布爾值 |
Boolean |
浮點數 |
Float |
整數 |
Long |
對象 |
Object |
數組 |
由第一個非空數值的類型決定 |
空值 |
忽略 |
下面是具體推斷 demo
# 寫入文檔,查看 Mapping PUT mapping_test/_doc/1 { "firstName":"Chan", -------- Text "lastName":"Jackie", -------- Text "loginDate":"2018-07-24T10:29:48.103Z" --------- Data }
# 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
能否更改mapping的字段類型
分兩種情況:
- 新增加的字段
- dynamic設為true時,新增字段的文檔寫入時,Mapping同時被更新
- dynamic設為false時,Mapping不會被更新,新增字段的數據無法被索引,但是會出現在_source中
- dynamic設為strict,文檔將寫入失敗
- 已存在的字段,一旦數據被寫入,就不再支持修改字段定義
- Lucene本身的限制
如果希望更改字段類型,必須Reindex api,即:重建索引。在數據量多的時候,開銷將非常大
# dynamic設置為false PUT idx1 { "mapping":{ "_doc":{ "dynamic":"false" } } }
# 修改為dynamic為false PUT idx1/_mapping { "dynamic":false }
# 查看索引
GET idx1/_mapping
dynamic屬性和索引字段可變性的規則,我們可以總結如下:
dynamic |
true |
false |
strict |
文檔可索引 |
yes |
yes |
no |
字段可索引 |
yes |
no |
no |
Mapping被更新 |
yes |
no |
no |
顯式Mapping及常見參數
mappings 中 field 定義選擇
"field": { "type": "text", // 設置成false,字段將不會被索引 "index": "false", // 指定分詞器 "analyzer": "ik", // 字段級別的分數加權 "boost": 1.23, // 對not_analyzed字段,默認都是開啟,analyzed字段不能使用,對排序和聚合能提升較大性能,節約內存,如果您確定不需要對字段進行排序或聚合,或者從script訪問字段值,則可以禁用doc值以節省磁盤空間 "doc_values": false, //Elasticsearch 加載內存 fielddata 的默認行為是 延遲 加載 。 當 Elasticsearch 第一次查詢某個字段時,它將會完整加載這個字段所有 Segment 中的倒排索引到內存中,以便於以后的查詢能夠獲取更好的性能 "fielddata": { "loading": "eager" }, // 可以對一個字段提供多種索引模式,同一個字段的值,一個分詞,一個不分詞 "fields": { "keyword": { "type": "keyword", "ignore_above": 256 } }, // 超過100個字符的文本,將會被忽略,不被索引 "ignore_above": 100, // 設置是否此字段包含在_all字段中,默認是true,除非index設置成no選項 "include_in_all": true, // 4個可選參數docs(索引文檔號) ,freqs(文檔號+詞頻),positions(文檔號+詞頻+位置,通常用來距離查詢),offsets(文檔號+詞頻+位置+偏移量,通常被使用在高亮字段)分詞字段默認是position,其他的默認是docs "index_options": "docs", // 分詞字段默認配置,不分詞字段:默認{"enable":false},存儲長度因子和索引時boost,建議對需要參與評分字段使用 ,會額外增加內存消耗量 "norms": { "enable": true, "loading": "lazy" }, // 設置一些缺失字段的初始化值,只有string可以使用,分詞字段的null值也會被分詞 "null_value": "NULL", // 影響距離查詢或近似查詢,可以設置在多值字段的數據上火分詞字段上,查詢時可指定slop間隔,默認值是100 "position_increament_gap": 0, //是否單獨設置此字段的是否存儲而從_source字段中分離,默認是false,只能搜索,不能獲取值 "store": false, // 設置搜索時的分詞器,默認跟ananlyzer是一致的,比如index時用standard+ngram,搜索時用standard用來完成自動提示功能 "search_analyzer": "ik", // 默認是TF/IDF算法,指定一個字段評分策略,僅僅對字符串型和分詞類型有效 "similarity": "BM25", //默認不存儲向量信息,支持參數yes(term存儲),with_positions(term+位置),with_offsets(term+偏移量),with_positions_offsets(term+位置+偏移量) 對快速高亮fast vector highlighter能提升性能,但開啟又會加大索引體積,不適合大數據量用 "term_vector": "no" }
總結:
- 與域數據格式及約束相關的參數:normalizer,format,ignore_above,ignore_malformed,coerce
- 與索引相關的參數:index,dynamic,enabled
- 與存儲策略相關的參數:store, fielddata,doc_values
- 分析器相關參數:analyzer,search_analyzer
其它參數:boost,copy_to,null_value
對於這些參數的描述主要基於筆者的理解,可能有不准確之處。實際上這些參數與ES的實現機制(如存儲結構,索引結構密切有關),只能在實際應用中去慢慢體會
analyzer 分詞器(重點)和 search_analyzer
PUT /my_index { "mappings":{ "properties":{ "text":{ "type":"text", "fields":{ "english":{ "type":"text", "analyzer":"english", "search_analyzer":"english" } } } } } }
使用_analyze 測試分詞器
GET my_index/_analyze { "field":"text", "text":"The quick Brown Foxes." }
GET my_index/_analyze { "field":"text.english", "text":"The quick Brown Foxes." }
index:
index,可用於設置字段是否被索引,默認為true,false即為不可搜索。在下述例子中,mobile字段將不能被搜索到。
# index屬性控制 字段是否可以被索引 PUT user_test { "mappings":{ "properties":{ "firstName":{ "type":"text" }, "lastName":{ "type":"text" }, "mobile":{ "type":"text", "index":false } } } }
index_options參數
1、index_options的作用是用於控制倒排索引記錄的內容,有如下四種配置:
2、docs:只記錄doc id
3、freqs:記錄doc id 和term frequencies
4、positions:記錄doc id、 term frequencies和term position
5、offsets:記錄doc id、 term frequencies、term position、character offsets
text類型的默認配置為positions,其他默認為docs。記錄的內容越多,占據的空間越大。
null_value設置
# 設定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" }
# 查看結果,有且僅有_id為2的記錄 GET users/_search { "query":{ "match":{ "mobile":"NULL" } } }
copy_to
這個屬性用於將當前字段拷貝到指定字段
1、_all在7.x版本已經被copy_to所代替
2、可用於滿足特定場景
3、copy_to將字段數值拷貝到目標字段,實現類似_all的作用
4、copy_to的目標字段不出現在_source中
DELETE user_test #設置 Copy to PUT user_test { "mappings":{ "properties":{ "firstName":{ "type":"text", "copy_to":"fullName" }, "lastName":{ "type":"text", "copy_to":"fullName" } } } }
PUT user_test/_doc/1 { "firstName":"Ruan", "lastName":"Yiming" }
POST user_test/_search?q=fullName:(Ruan Yiming)
精確值和全文本
精確值(Exact Values) vs 全文本(Full Text)
精確值,包括數字、日期、具體的字符串(如“192.168.0.1”)
Elasticsearch中類型為keyword,索引時,不需要做特殊的分詞處理
全文本,非結構化的文本數據
Elasticsearch中類型為text,索引時,需要對其進行分詞處理
如下結構的數據,我們可以大致判斷出哪些是精確值,哪些是全文本。其中的200、info、debug都是精確值。而message的內容為全文本
{ "code":200, "message":"this is a error item, you can change your apollo config !", "content":{ "tags":[ "info", "debug" ] } }
小結
視圖庫中對象的字段不用進行全文檢索,也可以使用關系數據庫作為存儲容器,但需要對JSON數據進行反序列化解析相應字段入庫,查詢出庫時需要將多個字段序列化為JSON數據。
固然在編程時可以使用ORM和JSON序列化中間件來完成工作,但在海量請求下,效率會有影響。使用ES可以利用ES的restful接口和JSON存儲格式的天然特性以契合規范要求。
在視圖庫規范中有一些自定義的約束,這些涉及數據有效性檢驗的服務應該部署在ES入庫之前。在本實例中,更多的是把ES作為一個Nosql數據庫使用
一個完整的mapping 設置,關於分詞器定義會在后面學習到
{ "settings": { "analysis": { "analyzer": { "ik_pinyin_analyzer": { "type": "custom", "tokenizer": "ik_smart", "filter": ["my_pinyin"]#自定義filter }, "pinyin_analyzer": { "tokenizer": "shopmall_pinyin" }, "first_py_letter_analyzer": { "tokenizer": "first_py_letter" }, "full_pinyin_letter_analyzer": { "tokenizer": "full_pinyin_letter" }, "onlyOne_analyzer": { "tokenizer": "onlyOne_pinyin" } }, "tokenizer": { #自定義分詞器 "onlyOne_pinyin": { "type":"pinyin", "keep_separate_first_letter": "false", "keep_first_letter":"false" }, "shopmall_pinyin": { "keep_joined_full_pinyin": "true", "keep_first_letter": "true", "keep_separate_first_letter": "false", "lowercase": "true", "type": "pinyin", "limit_first_letter_length": "16", "keep_original": "true", "keep_full_pinyin": "true", "keep_none_chinese_in_joined_full_pinyin": "true" }, "first_py_letter": { "type": "pinyin", "keep_first_letter": true, "keep_full_pinyin": false, "keep_original": false, "limit_first_letter_length": 16, "lowercase": true, "trim_whitespace": true, "keep_none_chinese_in_first_letter": false, "none_chinese_pinyin_tokenize": false, "keep_none_chinese": true, "keep_none_chinese_in_joined_full_pinyin": true }, "full_pinyin_letter": { "type": "pinyin", "keep_separate_first_letter": false, "keep_full_pinyin": false, "keep_original": false, "limit_first_letter_length": 16, "lowercase": true, "keep_first_letter": false, "keep_none_chinese_in_first_letter": false, "none_chinese_pinyin_tokenize": false, "keep_none_chinese": true, "keep_joined_full_pinyin": true, "keep_none_chinese_in_joined_full_pinyin": true } }, "filter": { "my_pinyin": { "type": "pinyin", "keep_joined_full_pinyin": true, "keep_separate_first_letter":true } } } }, "mappings": { "doc": { #type名字 "properties": {#mapping的屬性 "productName": { 屬性名字 "type": "text", #屬性類型 "analyzer": "ik_pinyin_analyzer", #分詞器 "fields": { #fields 指定自定義分詞器 查詢時通過productName.keyword_once_pinyin 可以指定 "keyword_once_pinyin": { "type": "text", "analyzer": "onlyOne_analyzer" #指定的自定義分詞器 } } }, "skuNames": { "type": "text", "analyzer": "ik_pinyin_analyzer", "fields": { "keyword_once_pinyin": { "type": "text", "analyzer": "onlyOne_analyzer" } } }, "regionCode": { "type": "keyword" }, "productNameSuggester": { #es6.x搜索建議實現 "type": "completion", "fields": { "pinyin": { "type": "completion", "analyzer": "pinyin_analyzer" }, "keyword_pinyin": { "type": "completion", "analyzer": "full_pinyin_letter_analyzer" }, "keyword_first_py": { "type": "completion", "analyzer": "first_py_letter_analyzer" } } } "info": { #es6父子類型設置 "type": "join", "relations": { "md_product":[ "sl_customer_character_order_list", "ic_product_store_account", "sl_customer_product_setting" ] } } } } } }