Elasticsearch從入門到放棄:瞎說Mapping

前面咱們聊了 Elasticsearch 的索引、搜索和分詞器,今天再來聊另外一個基礎內容—— Mapping。web

Mapping 在 Elasticsearch 中的地位至關於關係型數據庫中的 schema,它能夠用來定義索引中字段的名字、定義字段的數據類型,還能夠用來作一些字段的配置。從 Elasticsearch 7.0開始,Mapping 中不在意須要定義 type 信息了,具體緣由能夠看官方的解釋。數據庫

字段的數據類型

咱們剛剛提到 Mapping 中能夠定義字段的數據類型,這多是 Mapping 最經常使用的功能了,因此咱們先來看看 Elasticsearch 都支持哪些數據類型。數組

  • 簡單類型:text、keyword、date、long、double、boolean、ip
  • 複雜類型:對象類型、嵌套類型
  • 特殊類型:用於描述地理位置的 geo_point、geo_shape

Elasticsearch 支持的數據類型遠不止這些,因爲篇幅緣由,這裏就不一一列舉了。我找幾個工做中常見的來介紹一下。微信

首先就是字符串了,Elasticsearch 中的字符串有 text 和 keyword 兩種。其中 text 類型的字符串是能夠被全文檢索的,它會被分詞器做用,session

PUT my_index
{
  "mappings": {
    "properties": {
      "full_name": {
        "type":  "text"
      }
    }
  }
}

在設置字段類型爲 text 時,還能夠利用一些參數對這個字段進行更進一步的定製。app

index:標記這個字段是否能被搜索,默認是 true編輯器

search_analyzer:被搜索時所使用的分詞器,默認使用 setting 中設置的分詞器flex

fielddata:字段是否容許在內存中進行排序、聚合,默認是 falseurl

meta:關於字段的一些元數據spa

像一些id、郵箱、域名這樣的字段,咱們就須要使用 keyword 類型了。由於 keyword 類型能夠支持排序、聚合,而且只能支持精確查詢。

有些同窗可能會把 ID 設置爲數字類型,這也是沒問題的,數字類型和 keyword 各有各的好處,使用數字類型能夠進行範圍查找,而使用 keyword 類型則有更高的查詢效率。具體用哪一種還要看使用場景。

日期類型在 Elasticsearch 中有三種表現形式

  1. 能夠格式化成日期類型的字符串,如 "2020-07-26""2015/01/01 12:10:30"這樣的
  2. 毫秒級時間戳用 long 類型表示
  3. 秒級時間戳用 integer 類型表示

在 Elasticsearch 內部,日期類型是以 long 類型的毫秒級時間戳存儲的,時區使用的是0時區。

咱們能夠自定義時間格式,默認使用的是strict_date_optional_time||epoch_millis

「strict_date_optional_time_nanos」是通用的日期格式解析,至少要包含年份,若是要包含時間,則用T分隔,例如yyyy-MM-dd'T'HH:mm:ss.SSSSSSZyyyy-MM-dd

若是想要同時支持多種日期格式,可使用format字段

PUT my_index
{
  "mappings": {
    "properties": {
      "date": {
        "type":   "date",
        "format""yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
      }
    }
  }
}

Mapping參數

剛纔咱們提到配置 Mapping 的日期格式的參數format,Mapping 還提供了不少其餘的參數。

  • analyzer
  • boost
  • coerce
  • copy_to
  • doc_values
  • dynamic
  • eager_global_ordinals
  • enabled
  • fielddata
  • fields
  • format
  • ignore_above
  • ignore_malformed
  • index_options
  • index_phrases
  • index_prefixes
  • index
  • meta
  • normalizer
  • norms
  • null_value
  • position_increment_gap
  • properties
  • search_analyzer
  • similarity
  • store
  • term_vector

咱們來介紹幾個經常使用的字段。

fields

首先是fields,它可使同一個字段經過不一樣的方式實現不一樣的目的。

例如,咱們能夠對一個字符串字段設置爲text類型,用於全文檢索,同時能夠利用fields設置爲keyword類型,用於排序和聚合。

PUT my-index-000001
{
  "mappings": {
    "properties": {
      "city": {
        "type""text",
        "fields": {
          "raw": {
            "type":  "keyword"
          }
        }
      }
    }
  }
}

查詢時咱們就可使用city進行全文檢索,使用city.raw進行排序和聚合。

GET my-index-000001/_search
{
  "query": {
    "match": {
      "city""york" 
    }
  },
  "sort": {
    "city.raw""asc" 
  },
  "aggs": {
    "Cities": {
      "terms": {
        "field""city.raw" 
      }
    }
  }
}

enabled

有些時候,咱們只想把某個字段做爲數據存儲來使用,並不須要用來作搜索,這時,咱們就能夠將這個字段禁用掉,字段被禁用之後,它所保存的值也不受 mapping 指定的類型控制。

PUT my-index-000001
{
  "mappings": {
    "properties": {
      "user_id": {
        "type":  "keyword"
      },
      "last_updated": {
        "type""date"
      },
      "session_data": { 
        "type""object",
        "enabled"false
      }
    }
  }
}

上面的例子中,咱們禁用掉了 session_data 這個字段,這時,你既能夠往 session_data 字段中存儲 JSON 格式的數據,也能夠存儲非 JSON 格式的數據。

除了針對於單個字段的禁用之外,咱們還能夠直接禁用掉整個 mapping。咱們來從新建立一個index

PUT my-index-000002
{
  "mappings": {
    "enabled"false 
  }
}

這時,文檔全部的字段都不會被索引,只是用來存儲。

須要注意的是,不管是具體字段中仍是整個 mapping 的 enabled 屬性都不能夠被修改,由於一旦設置爲 false,Elasticsearch 就不會對字段進行索引了,也不會校驗數據的合法性,若是產生了髒數據之後再設置爲 true,就會形成程序錯誤。

null_value

null 在 Elasticsearch 中是不能夠被索引或搜索的,這裏咱們所說的 null 並非狹義上某種語言的 null,而是全部的空值。例如全部值都是 null 的數組,總之,這裏的定義就是沒有值。

對於有須要搜索空值的業務怎麼辦呢?Elasticsearch 爲咱們提供了 null_value 這個參數,它能夠指定一個值,搜索時使用這個值來替代空值。

舉個栗子

PUT my-index-000001
{
  "mappings": {
    "properties": {
      "status_code": {
        "type":       "keyword",
        "null_value""NULL" 
      }
    }
  }
}

咱們給 status_code 字段設置了 null_value"NULL"。這裏須要注意, null_value 的類型必須與要查找的數據類型相同,若是在這個例子中 status_code 的類型是long,那麼就不能把null_value 設置爲 "NULL"

dynamic

對於新增長的字段:

  • dynamic 設置爲 true 時,一旦有新增字段的文檔寫入,Mapping 也會被更新
  • dynamic 設置爲 false 時,Mapping 不會被更新,新增字段沒法被索引,但信息會出如今 _source
  • dynamic 設置爲 strict 時,文檔寫入失敗

對於已有的字段,一旦已經有數據寫入,就再也不支持修改字段定義

Dynamic Mapping

咱們在建立索引時,能夠不用手動寫 Mappings, Elasticsearch 會幫咱們自動識別出字段的類型。咱們稱之爲 Dynamic Mapping。不過有時推算的可能不是很準確。

Elasticsearch 自動識別類型是基於 JSON 的。數據類型的對應關係以下(表格來自 elastic 官網)

「JSON data type」 「Elasticsearch data type」
null No field is added.
true or false boolean field
floating point number float field
integer long field
object object field
array Depends on the first non-null value in the array.
string Either a date field (if the value passes date detection), a double or long field (if the value passes numeric detection) or a text field, with a keyword sub-field.

Elasticsearch 支持的字段映射的數據類型在這個文檔中,除了這些,其餘的類型映射都須要顯示的指定了。

關於日期類型,默認是能夠映射的,可是 Elasticsearch 只能識別幾種格式的日期yyyy/MM/dd HH:mm:ss||yyyy/MM/dd||epoch_millis。若是關掉了 date_detection 開關,那麼就只能識別爲字符串了。

PUT my-index-000001
{
  "mappings": {
    "date_detection"false
  }
}

固然,你也能夠根據須要本身指定要識別的日期格式,只須要使用 dynamic_date_formats 參數便可。

PUT my-index-000001
{
  "mappings": {
    "dynamic_date_formats": ["MM/dd/yyyy"]
  }
}

Elasticsearch 還提供了一種把字符串型的數字識別爲數字的能力,它是由 numeric_detection 開關控制的。

PUT my-index-000005
{
  "mappings": {
    "numeric_detection"true
  }
}

PUT my-index-000005/_doc/1
{
  "my_float":   "1.0"
  "my_integer""1" 
}

在這個例子中,my_float 會被識別爲 float 類型,而 my_integer 會被識別爲 long 類型。

Dynamic template

dynamic template 容許咱們自定義 mapping ,並應用到具體索引上。dynamic template 的定義通常是這樣的

  "dynamic_templates": [
    {
      "my_template_name": { 
        ...  match conditions ... 
        "mapping": { ... } 
      }
    },
    ...
  ]

my_template_name 能夠是任意字符串。

match conditions 包括match_mapping_type, match, match_pattern, unmatch, path_match, path_unmatch 這幾種。

mapping 就是指匹配到的字段應該使用怎樣的 mapping。下面咱們介紹幾種 match conditions

match_mapping_type

咱們先來看一個簡單的例子

PUT my-index-000001
{
  "mappings": {
    "dynamic_templates": [
      {
        "integers": {
          "match_mapping_type""long",
          "mapping": {
            "type""integer"
          }
        }
      },
      {
        "strings": {
          "match_mapping_type""string",
          "mapping": {
            "type""text",
            "fields": {
              "raw": {
                "type":  "keyword",
                "ignore_above": 256
              }
            }
          }
        }
      }
    ]
  }
}

這裏咱們有兩個模版,其一是使用 integer 類型來代替 long 類型,其二是將字符串類型映射爲 keyword

match 和 unmatch

這兩個比較簡單,match 是指匹配到模式的字段, unmatch 是表示不匹配的字段。

PUT my-index-000001
{
  "mappings": {
    "dynamic_templates": [
      {
        "longs_as_strings": {
          "match_mapping_type""string",
          "match":   "long_*",
          "unmatch""*_text",
          "mapping": {
            "type""long"
          }
        }
      }
    ]
  }
}

在這個例子中,咱們須要的是 long_ 開頭的字符串,不須要 _text結尾的字符串字段。

除了以上三種以外,其餘的就是 match_pattern 用來進行正則匹配,path_matchpath_unmatch 則是表示字段所在路徑的是否匹配。

另外 dynamic template 還支持兩種變量替換,分別是 {name}{dynamic_type}。其實 name 就是字段名,dynamic_type 就是檢測出的字段類型。

總結

關於 Elasticsearch 的 mapping 咱們就先聊這些,我認爲 mapping 的配置是一個須要經驗的事情,當你處理的 case 愈來愈多以後,就能比較輕鬆的知道如何更好的配置 mapping 了。此外,mapping 的許多字段和參數文中都沒有涉及,對於我而言,大部分都是用到了現查文檔,不過也仍是建議你們看一看文檔,起碼遇到問題時能知道大概查找文檔的一個方向。這樣就會比身邊人強很多。

往期精彩回顧
Elasticsearch從入門到放棄:索引基本使用方法
Elasticsearch從入門到放棄:文檔CRUD要牢記
Elasticsearch從入門到放棄:分詞器初印象
Elasticsearch從入門到放棄:再聊搜索


本文分享自微信公衆號 - 代碼潔癖患者(Jackeyzhe2018)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索