天天看點

Elasticsearch 通關教程(二): 索引映射Mapping問題字段類型映射動态映射靜态映射(顯式映射)

資料庫建表的時候,我們的DDL語句一般都會指定每個字段的存儲類型,例如:varchar,int,datetime等等,目的很明确,就是更精确的存儲資料,防止資料類型格式混亂。

CREATE TABLE `shop_` (
  `id_` varchar(36) NOT NULL COMMENT 'id',
  `shop_name_` varchar(50) DEFAULT NULL COMMENT '商品名稱',
  `shop_integral_` int(11) DEFAULT NULL COMMENT '兌換所需積分',
  `shop_money_` decimal(10,0) DEFAULT NULL COMMENT '劵面金額',
  `start_time_` datetime DEFAULT NULL COMMENT '有效開始時間',
  `end_time_` datetime DEFAULT NULL COMMENT '有效結束時間',
  `is_delete_` int(1) DEFAULT '1' COMMENT '是否删除-1:有效,0:删除',
  PRIMARY KEY (`id_`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;
           

在 Elasticsearch中也是這樣,建立索引的時候一般也需要指定索引的字段類型,這種方式成為映射(Mapping)。

字段類型

映射(Mapping)針對的是文檔的字段,資料庫中有varchar,int,datetime等資料類型,那麼我們ElasticSearch中又有哪些字段類型,每個字段類型都代表什麼意思呢?

ElasticSearch更新頻繁,以下内容是針對6.x版本的,對于5.x版本以及之前的版本可能有所不同,未來7.x版本也許也會有所改變,本篇不能做到面面俱到,是以大家可以針對自己的版本查閱官方文檔。

Elasticsearch支援文檔字段的多種不同資料類型,根據官方文檔的分類,可以劃分為以下幾個類别:

核心資料類型,複雜資料類型,Geo(地理)資料類型,專用資料類型和多字段。

核心資料類型

  • 字元串類型

    主要包括:text 和 keyword。

  • 數字類型

    主要包括:long, integer, short, byte, double, float, half_float, scaled_float

  • 日期類型
  • 布爾類型
  • 二進制類型
  • 範圍資料類型

    integer_range, float_range, long_range, double_range, date_range

這裡我們重點介紹下 text 和 keyword 的差別:

text

用于索引全文值的字段,例如電子郵件正文或産品說明。這些字段是analyzed,它們通過分詞器傳遞 ,以在被索引之前将字元串轉換為單個術語的清單。分析過程允許Elasticsearch搜尋單個單詞中 每個完整的文本字段。文本字段不用于排序,很少用于聚合(盡管 重要的文本聚合 是一個值得注意的例外)。

keyword

用于索引結構化内容的字段,例如電子郵件位址,主機名,狀态代碼,郵政編碼或标簽。它們通常用于過濾,排序,和聚合。keyword字段隻能按其确切值進行搜尋。如果您需要索引電子郵件正文或産品說明等全文内容,則可能應該使用text字段。

有時候一個字段同時擁有全文類型(text)和關鍵字類型(keyword)是有用的:一個用于全文搜尋,另一個用于聚合和排序。這可以通過多字段類型來實作。

複雜資料類型

Geo資料類型

專用資料類型

多字段

有時候單純的一個字段類型滿足不了我們複雜的需求,為了不同的目的,以不同的方式索引同一個字段通常很有用。多字段也是ES的一種資料類型,隻不過結合了更多的功能。

例如,對于字元串字段,我們既可以将它映射為text類型用于全文搜尋,亦可以将它映射為keyword類型用于排序或聚合,或者,還可以使用标準分詞器、英語分詞器和其他語言分詞器索引文本字段。

大多數資料類型都通過

fields

參數支援多字段。例如對于城市名稱的多字段映射,可以這樣寫:

PUT my_index
{
  "mappings": {
    "_doc": {
      "properties": {
        "cityName": {
          "type": "text",
          "fields": {
            "raw": { 
              "type":  "keyword"
            }
          }
        }
      }
    }
  }
}           

Elasticsearch的字段類型講解完了,我們接下來正式介紹 ES的映射,ES是如何将索引文檔和資料類型進行關聯的,建立索引前是否必須制定索引文檔的資料類型呢?

映射

映射是定義一個文檔及其包含的字段如何存儲和索引的過程。例如,使用映射來定義:

  • 應将哪些字元串字段視為全文字段。
  • 哪些字段包含數字,日期或地理位置。
  • 是否應将文檔中所有字段的值索引到catch-all _all字段中。
  • 日期值的格式。
  • 自定義規則以控制動态添加字段的映射。

其實在 ElasticSearch中可以不需要事先定義映射(Mapping),文檔寫入ElasticSearch時,會根據文檔字段自動識别類型,但是通過這種自動識别的字段不是很精确,對于一些複雜的需要分詞的就不适合了。

根據是否自動識别映射類型,我們可以将映射分為動态映射和靜态映射。

動态映射,即不事先指定映射類型(Mapping),文檔寫入ElasticSearch時,ES會根據文檔字段自動識别類型,這種機制稱之為動态映射。

靜态映射,即人為事先定義好映射,包含文檔的各個字段及其類型等,這種方式稱之為靜态映射,亦可稱為顯式映射。

動态映射

Elasticsearch最重要的功能之一是它試圖擺脫你的方式,讓你盡快開始探索你的資料。Elasticsearch試圖讓你成功安裝環境之後就可以直接使用。要索引文檔,您不必首先建立索引、定義映射類型和定義字段,其實您隻需索引一個文檔資料,然後索引、類型和字段将自動生效。

索引一個圖書的文檔:

PUT /library/book/1
{
  "bookId":1,
  "bookName":"Java核心技術 卷I",
  "publishDate":"2014-03-12"
}           

傳回結果如下,表示成功

{
  "_index": "library",
  "_type": "book",
  "_id": "1",
  "_version": 1,
  "result": "created",
  "_shards": {
    "total": 2,
    "successful": 1,
    "failed": 0
  },
  "_seq_no": 0,
  "_primary_term": 1
}           

我們看下mapping映射資訊

GET library/_mapping           

得到如下映射資訊,重點關注mapping節點的内容

{
  "library": {
    "mappings": {
      "book": {
        "properties": {
          "bookId": {
            "type": "long"
          },
          "bookName": {
            "type": "text",
            "fields": {
              "keyword": {
                "type": "keyword",
                "ignore_above": 256
              }
            }
          },
          "publishDate": {
            "type": "date"
          }
        }
      }
    }
  }
}           

可以看到,我們并沒有建立索引映射,Elasticsearch自動根據文檔資料為我們映射了字段類型,bookId的映射類型為long,bookName的映射類型為多字段的即為text,同時也為keyword,publishDate的映射類型為date。可以看到ES的動态映射功能還是蠻強大的。

預設情況下,當在文檔中找到以前未見過的字段時,Elasticsearch會自動将這個新字段添加到類型映射中。我們可以在文檔和object級别禁用這項功能,具體操作方式就是通過将

dynamic

參數設定為

false

strict

,設為false是忽略新字段,而設為strict是如果遇到未知字段,就抛出異常。

假設啟用了動态字段映射功能,則使用一些簡單的規則來确定字段應具有的資料類型:

JSON datatype Elasticsearch datatype
null 沒有字段添加
true or false boolean
integer long
object
array 依賴于數組中首個非空值
string 可以是日期字段、double或long字段,也可以是帶有關鍵字子字段的文本字段。

上面這些是可以動态檢測到的字段資料類型,而其他的以外的字段必須要顯式映射資料類型了。

對于string字元串字段,動态映射的結果會有多種,可能映射為日期類型,也可能映射為double或long類型,也可能映射為帶有關鍵字的text類型,具體結果要看配置的檢測類型,是日期檢測還是數字檢測。

日期檢測

如果

date_detection

啟用(預設),則檢查新字元串字段以檢視其内容是否與

dynamic_date_formats

指定的任何日期模式比對 。如果找到比對項,那麼則添加為具有對應格式的date新字段。

預設值為

dynamic_date_formats:[ "strict_date_optional_time","yyyy/MM/dd HH:mm:ss Z||yyyy/MM/dd Z"]           

例如:

PUT my_index/_doc/1
{
  "create_date": "2015/09/02"
}           

通過

GET my_index/_mapping

得到的結果為:

{
  "my_index": {
    "mappings": {
      "_doc": {
        "properties": {
          "city": {
            "type": "text",
            "fields": {
              "raw": {
                "type": "keyword"
              }
            }
          },
          "create_date": {
            "type": "date",
            "format": "yyyy/MM/dd HH:mm:ss||yyyy/MM/dd||epoch_millis"
          }
        }
      }
    }
  }
}           

動态日期檢測可以通過設定

date_detection

false

來禁用:

PUT my_index
{
  "mappings": {
    "_doc": {
      "date_detection": false
    }
  }
}

PUT my_index/_doc/1 
{
  "create": "2015/09/02"
}           

禁用之後,重新擷取映射類型,得到如下結果:

{
  "my_index": {
    "mappings": {
      "_doc": {
        "date_detection": false,
        "properties": {
          "create": {
            "type": "text",
            "fields": {
              "keyword": {
                "type": "keyword",
                "ignore_above": 256
              }
            }
          }
        }
      }
    }
  }
}           

這時,create_date字段已被添加為文本字段。我們也可以自定義檢測到的日期格式,通過

dynamic_date_formats

可以自定義以支援您自己的日期格式:

PUT my_index 
{ 
  "mappings":{ 
    "_ doc":{ 
      "dynamic_date_formats":["MM / dd / yyyy"] 
    } 
  } 
}            

數字檢測

雖然JSON支援本機浮點和整數資料類型,但某些應用程式或語言有時可能将數字呈現為字元串。通常,正确的解決方案是顯式映射這些字段,但可以啟用數字檢測(預設情況下禁用)以自動執行此操作:

PUT my_index 
{ 
  "mappings":{ 
    "_ doc":{ 
      "numeric_detection":true 
    } 
  } 
} 

PUT my_index / _doc / 1 
{ 
  "my_float":"1.0",
  "my_integer":"1" 
}           

其中my_float字段将添加為float字段,my_integer字段将添加為long字段。

除了上面列出的選項外,還可以進一步自定義動态字段映射規則

dynamic_templates

,動态模闆允許您定義可應用于動态添加字段的自定義映射,具體取決于:

  • Elasticsearch檢測到 的資料類型match_mapping_type。
  • 字段的名稱,帶match和unmatch或match_pattern。
  • 字段的完整虛線路徑,帶path_match和path_unmatch。

更多具體内容可參考官方文檔,

Dynamic templates一章

這裡就不多叙述了。

靜态映射(顯式映射)

動态映射的自動類型推測功能并不是100%正确的,這就需要靜态映射機制。靜态映射與關系資料庫中建立表語句類型,需要事先指定字段類型。相對于動态映射,靜态映射可以添加更加詳細字段類型、更精準的配置資訊等。

既然可以自定義映射字段類型,那麼那些複雜的字段類型和分詞器我們都可以根據自己需求添加了,以提供了字段映射使用的各種映射參數的詳細說明,這些映射參數對于某些或所有字段資料類型是通用的,内容太多,這裡感興趣的讀者可以點選具體連結閱讀。

映射參數 說明
analyzer 分析器
normalizer 在 Elasticsearch 中處理字元串類型的資料時,如果我們想把整個字元串作為一個完整的 term 存儲,我們通常會将其類型 type 設定為 keyword。但有時這種設定又會給我們帶來麻煩,比如同一個資料再寫入時由于沒有做好清洗,導緻大小寫不一緻,比如 apple、Apple兩個實際都是 apple,但當我們去搜尋 apple時卻無法傳回 Apple的文檔。要解決這個問題,就需要 Normalizer出場了。
boost 單個字段可以自動提升以計數更多的相關性得分
coerce 強制嘗試清除髒值以适合字段的資料類型。資料并不總是幹淨的,根據它的生成方式,數字可能會在JSON正文中呈現為真正的JSON數字,例如5,但它也可能呈現為字元串,例如"5"。或者,應該是整數的數字可以替代地呈現為浮點,例如5.0,或甚至 "5.0"。
copy_to copy_to參數允許您建立自定義 _all字段,可以将多個字段的值複制到組字段中,然後可以将其作為單個字段進行查詢。
doc_values
dynamic 設定動态映射
enabled enabled設定隻能應用于映射類型和 object字段,導緻Elasticsearch完全跳過對字段内容的解析
fielddata
eager_global_ordinals
format 格式化日期
ignore_above
ignore_malformed
index_options
index
fields
norms
null_value 當字段設定為null,(或空數組或null值數組)時,它被視為該字段沒有值。不能被索引或搜尋
position_increment_gap
properties
search_analyzer
similarity
store
term_vector