大概閱讀10分鐘
html
本教程是系列教程,對於初學者能夠對 ES 有一個總體認識和實踐實戰。算法
還沒開始的同窗,建議先讀一下系列攻略目錄:Springboot2.x整合ElasticSearch7.x實戰目錄數據庫
本篇幅是繼上一篇 Springboot2.x整合ElasticSearch7.x實戰(二) ,適合初學 Elasticsearch 的小白,能夠跟着整個教程作一個練習。json
[toc]api
Mapping 是整個 ES 搜索引擎中最重要的一部分之一,學會構建一個好的索引,可讓咱們的搜索引擎更高效,更節省資源。數組
Mapping 是Elasticsearch 中一種術語, Mapping 相似於數據庫中的表結構定義 schema,它有如下幾個做用:bash
1. 定義索引中的字段的名稱 2. 定義字段的數據類型,好比字符串、數字、布爾 3. 字段,倒排索引的相關配置,好比設置某個字段爲不被索引、記錄 position(位置) 等
在 ES 早期版本,一個索引下是能夠有多個 Type ,從 7.0 開始,一個索引只有一個 Type,也能夠說一個 Type 有一個 Mapping 定義。app
瞭解了什麼是 Mapping 後,接下來對 Mapping 的設置坐下介紹:curl
官網參考:https://www.elastic.co/guide/en/elasticsearch/reference/7.1/mapping.htmlelasticsearch
PUT users { "mappings": { "_doc": { "dynamic": false } } }
在建立一個索引的時候,能夠對 dynamic 進行設置,能夠設成 false、true 或者 strict。
好比一個新的文檔,這個文檔包含一個字段,當 Dynamic
設置爲 true 時,這個文檔能夠被索引進 ES,這個字段也能夠被索引,也就是這個字段能夠被搜索,Mapping 也同時被更新;當 dynamic 被設置爲 false 時候,存在新增字段的數據寫入,該數據能夠被索引,可是新增字段被丟棄;當設置成 strict 模式時候,數據寫入直接出錯。
另外還有 index
參數,用來控制當前字段是否被索引,默認爲 true,若是設爲 false(有些業務場景,某些字段不但願被搜索到),則該字段不可被搜索。
# index屬性控制 字段是否能夠被索引 PUT user_test { "mappings": { "properties": { "firstName":{ "type": "text" }, "lastName":{ "type": "text" }, "mobile" :{ "type": "text", "index": false } } } }
參數 index_options 用於控制倒排索引記錄的內容,有以下 4 種配置:
另外,text 類型默認配置爲 positions,其餘類型默認爲 doc,記錄內容越多,佔用存儲空間越大。
null_value 主要是當字段遇到 null 值時的處理策略,默認爲 NULL,即空值,此時 ES 會默認忽略該值,能夠經過設定該值設定字段的默認值,另外只有 KeyWord 類型支持設定 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" } } }
這個屬性如今使用不多,不作深刻講解
參考官網:https://www.elastic.co/guide/cn/elasticsearch/guide/current/root-object.html
這個屬性用於將當前字段拷貝到指定字段。
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" } //沒有新建字段 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" } } } } '
一些要點:
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." }
咱們知道 Mapping 是能夠經過咱們插入的文檔自動生成索引,可是可能仍是有一些問題。例如:生成的字段類型不正確,字段的附加屬性不知足咱們的需求。這是咱們能夠經過顯式Mapping的方式來解決。倆種方法:
推薦第二種,不容易出錯,效率高。
ES 類型的自動識別是基於 JSON 的格式,若是輸入的是 JSON 是字符串且格式爲日期格式,ES 會自動設置成 Date 類型;當輸入的字符串是數字的時候,ES 默認會當成字符串來處理,能夠經過設置來轉換成合適的類型;若是輸入的是 Text 字段的時候,ES 會自動增長 keyword 子字段,還有一些自動識別以下圖所示:
# 寫入文檔,查看 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
mappings 中field定義選擇:
"field": { "type": "text", //文本類型 "index": "false"// ,設置成false,字段將不會被索引 "analyzer":"ik"//指定分詞器 "boost":1.23//字段級別的分數加權 "doc_values":false//對not_analyzed字段,默認都是開啓,analyzed字段不能使用,對排序和聚合能提高較大性能,節約內存,若是您肯定不須要對字段進行排序或聚合,或者從script訪問字段值,則能夠禁用doc值以節省磁盤空間: "fielddata":{"loading" : "eager" }//Elasticsearch 加載內存 fielddata 的默認行爲是 延遲 加載 。 當 Elasticsearch 第一次查詢某個字段時,它將會完整加載這個字段全部 Segment 中的倒排索引到內存中,以便於之後的查詢可以獲取更好的性能。 "fields":{"keyword": {"type": "keyword","ignore_above": 256}} //能夠對一個字段提供多種索引模式,同一個字段的值,一個分詞,一個不分詞 "ignore_above":100 //超過100個字符的文本,將會被忽略,不被索引 "include_in_all":ture//設置是否此字段包含在_all字段中,默認是true,除非index設置成no選項 "index_options":"docs"//4個可選參數docs(索引文檔號) ,freqs(文檔號+詞頻),positions(文檔號+詞頻+位置,一般用來距離查詢),offsets(文檔號+詞頻+位置+偏移量,一般被使用在高亮字段)分詞字段默認是position,其餘的默認是docs "norms":{"enable":true,"loading":"lazy"}//分詞字段默認配置,不分詞字段:默認{"enable":false},存儲長度因子和索引時boost,建議對須要參與評分字段使用 ,會額外增長內存消耗量 "null_value":"NULL"//設置一些缺失字段的初始化值,只有string可使用,分詞字段的null值也會被分詞 "position_increament_gap":0//影響距離查詢或近似查詢,能夠設置在多值字段的數據上火分詞字段上,查詢時可指定slop間隔,默認值是100 "store":false//是否單獨設置此字段的是否存儲而從_source字段中分離,默認是false,只能搜索,不能獲取值 "search_analyzer":"ik"//設置搜索時的分詞器,默認跟ananlyzer是一致的,好比index時用standard+ngram,搜索時用standard用來完成自動提示功能 "similarity":"BM25"//默認是TF/IDF算法,指定一個字段評分策略,僅僅對字符串型和分詞類型有效 "term_vector":"no"//默認不存儲向量信息,支持參數yes(term存儲),with_positions(term+位置),with_offsets(term+偏移量),with_positions_offsets(term+位置+偏移量) 對快速高亮fast vector highlighter能提高性能,但開啓又會加大索引體積,不適合大數據量用 }
總結一下:
對於這些參數的描述主要基於筆者的理解,可能有不許確之處。實際上這些參數與ES的實現機制(如存儲結構,索引結構密切有關),只能在實際應用中去慢慢體會。
ES 字段類型相似於 MySQL 中的字段類型,ES 字段類型主要有:核心類型、複雜類型、地理類型以及特殊類型,具體的數據類型以下圖所示:
從圖中能夠看出核心類型能夠劃分爲字符串類型、數字類型、日期類型、布爾類型、基於 BASE64 的二進制類型、範圍類型。
其中,在 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 中日期能夠爲如下形式:
格式化的日期字符串,例如 2020-03-17 00:00、2020/03/17
時間戳(和 1970-01-01 00:00:00 UTC 的差值),單位毫秒或者秒
即便是格式化的日期字符串,ES 底層依然採用的是時間戳的形式存儲。
JSON 文檔中一樣存在布爾類型,不過 JSON 字符串類型也能夠被 ES 轉換爲布爾類型存儲,前提是字符串的取值爲 true 或者 false,布爾類型經常使用於檢索中的過濾條件。
二進制類型 binary 接受 BASE64 編碼的字符串,默認 store 屬性爲 false,而且不能夠被搜索。
範圍類型能夠用來表達一個數據的區間,能夠分爲5種:integer_range、float_range、long_range、double_range 以及 date_range。
複合類型主要有對象類型(object)和嵌套類型(nested):
JSON 字符串容許嵌套對象,一個文檔能夠嵌套多個、多層對象。能夠經過對象類型來存儲二級文檔,不過因爲 Lucene 並無內部對象的概念,ES 會將原 JSON 文檔扁平化,例如文檔:
{ "name": { "first": "wu", "last": "px" } }
實際上 ES 會將其轉換爲如下格式,並經過 Lucene 存儲,即便 name 是 object 類型:
{ "name.first": "wu", "name.last": "px" }
嵌套類型能夠當作是一個特殊的對象類型,可讓對象數組獨立檢索,例如文檔:
{ "group": "users", "username": [ { "first": "wu", "last": "px"}, { "first": "hu", "last": "xy"}, { "first": "wu", "last": "mx"} ] }
username 字段是一個 JSON 數組,而且每一個數組對象都是一個 JSON 對象。若是將 username 設置爲對象類型,那麼 ES 會將其轉換爲:
{ "group": "users", "username.first": ["wu", "hu", "wu"], "username.last": ["px", "xy", "mx"] }
能夠看出轉換後的 JSON 文檔中 first 和 last 的關聯丟失了,若是嘗試搜索 first 爲 wu,last 爲 xy 的文檔,那麼成功會檢索出上述文檔,可是 wu 和 xy 在原 JSON 文檔中並不屬於同一個 JSON 對象,應當是不匹配的,即檢索不出任何結果。
嵌套類型就是爲了解決這種問題的,嵌套類型將數組中的每一個 JSON 對象做爲獨立的隱藏文檔來存儲,每一個嵌套的對象都可以獨立地被搜索,因此上述案例中雖然表面上只有 1 個文檔,但其實是存儲了 4 個文檔。
地理類型字段分爲兩種:經緯度類型和地理區域類型:
經緯度類型字段(geo_point)能夠存儲經緯度相關信息,經過地理類型的字段,能夠用來實現諸如查找在指定地理區域內相關的文檔、根據距離排序、根據地理位置修改評分規則等需求。
經緯度類型能夠表達一個點,而 geo_shape 類型能夠表達一塊地理區域,區域的形狀能夠是任意多邊形,也能夠是點、線、面、多點、多線、多面等幾何類型。
特殊類型包括 IP 類型、過濾器類型、Join 類型、別名類型等,在這裏簡單介紹下 IP 類型和 Join 類型,其餘特殊類型能夠查看官方文檔。
IP 類型的字段能夠用來存儲 IPv4 或者 IPv6 地址,若是須要存儲 IP 類型的字段,須要手動定義映射:
{ "mappings": { "properties": { "my_ip": { "type": "ip" } } } }
Join 類型是 ES 6.x 引入的類型,以取代淘汰的 _parent 元字段,用來實現文檔的一對1、一對多的關係,主要用來作父子查詢。
Join 類型的 Mapping 以下:
PUT my_index { "mappings": { "properties": { "my_join_field": { "type": "join", "relations": { "question": "answer" } } } } }
其中,my_join_field 爲 Join 類型字段的名稱;relations 指定關係:question 是 answer 的父類。
例如定義一個 ID 爲 1 的父文檔:
PUT my_join_index/1?refresh { "text": "This is a question", "my_join_field": "question" }
接下來定義一個子文檔,該文檔指定了父文檔 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