1. 程式人生 > >Elasticsearch 通關教程(二): 索引對映Mapping問題

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引數設定為falsestrict,設為false是忽略新欄位,而設為strict是如果遇到未知欄位,就丟擲異常。

假設啟用了動態欄位對映功能,則使用一些簡單的規則來確定欄位應具有的資料型別:

JSON datatype Elasticsearch datatype
null 沒有欄位新增
true or false boolean
integer long
object 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_detectionfalse來禁用:

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