在ElasticSearch中,存入文檔的內容類似於傳統數據每個字段一樣,都會有一個指定的屬性,為了能夠把日期字段處理成日期,把數字字段處理成數字,把字符串字段處理成字符串值,Elasticsearch需要知道每個字段里面都包含了什么類型。這些類型和字段的信息存儲(包含)在映射(mapping)中。
核心簡單字段類型
Elasticsearch支持以下簡單字段類型:
- String:string(棄用), text, keyword(ElasticSearch 5.0開始支持,先以string介紹,后面再介紹新類型)
- Whole number:byte, short, integer, long
- Floating-point:float, double
- Boolean:boolean
- Date:date
當你索引一個包含新字段的文檔——一個之前沒有的字段——Elasticsearch將使用動態映射猜測字段類型,這類型來自於JSON的基本數據類型,使用以下規則:
注意:這意味着,如果你索引一個帶引號的數字——"123",它將被映射為"string"類型,而不是"long"類型。然而,如果字段已經被映射為"long"類型,Elasticsearch將嘗試轉換字符串為long,並在轉換失敗時會拋出異常。
查看映射
我們可以使用_mapping
后綴來查看Elasticsearch中的映射。在本章開始我們已經找到索引blogs
類型dc
中的映射:
GET /blogs/_mapping/doc
這展示給了我們字段的映射(叫做屬性(properties)),這些映射是Elasticsearch在創建索引時動態生成的:
上面的結果是在Elasticsearch 5.0.2上運行的,我們先不用在意text
屬性的具體內容,只需要知道字符串被自動的映射成text
屬性,而在Elasticsearch 5之前的版本中應該是string
。
確切值vs全文
可能在數據庫中的習慣,只需要確定每個字段的類型即可,但是在Elasticsearch中每種字段的都有不同的索引處理方式。
Elasticsearch為對字段類型進行猜測,動態生成了字段和類型的映射關系。返回的信息顯示了date字段被識別為date類型。_all因為是默認字段所以沒有在此顯示,不過我們知道它是string(text)類型。Elasticsearch對每一種核心數據類型(string, number, boolean及date)以不同的方式進行索引。
但是更大的區別在於確切值(exact values)(比如string類型)及全文文本(full text)之間。這兩者的區別才真的很重要 - 這是區分搜索引擎和其他數據庫的根本差異。
Elasticsearch中的數據可以大致分為兩種類型:
確切值 及 全文文本。
確切值:是確定的,正如它的名字一樣。比如一個date或用戶ID,也可以包含更多的字符串比如username或email地址。
確切值"Foo"和"foo"就並不相同。確切值2014和2014-09-15也不相同。
全文文本:從另一個角度來說是文本化的數據(常常以人類的語言書寫),比如一篇推文(Twitter的文章)或郵件正文。
確切值是很容易查詢的,因為結果是二進制的 -- 要么匹配,要么不匹配。下面的查詢很容易以SQL表達:
WHERE name = "John Smith"
AND user_id = 2
AND date > "2014-09-15"
而對於全文數據的查詢來說,卻有些微妙。我們不會去詢問這篇文檔是否匹配查詢要求?。但是,我們會詢問這篇文檔和查詢的匹配程度如何?。換句話說,對於查詢條件,這篇文檔的_相關性_有多高?
我們很少確切的匹配整個全文文本。我們想在全文中查詢包含查詢文本的部分。不僅如此,我們還期望搜索引擎能理解我們的意圖:(由此引出了Elasticsearch提供了很好的相關性查詢,並以匹配度排序。)
- 一個針對"UK"的查詢將返回涉及"United Kingdom"的文檔
- 一個針對"jump"的查詢同時能夠匹配"jumped", "jumps", "jumping"甚至"leap"
- "johnny walker"也能匹配"Johnnie Walker", "johnnie depp"及"Johnny Depp"
- "fox news hunting"能返回有關hunting on Fox News的故事,而"fox hunting news"也能返回關於fox hunting的新聞故事。
為了方便在全文文本字段中進行這些類型的查詢,Elasticsearch首先對文本分析(analyzes),然后使用結果建立一個倒排索引。
倒排索引
Elasticsearch使用一種叫做倒排索引(inverted index)的結構來做快速的全文搜索。倒排索引由在文檔中出現的唯一的單詞列表,以及對於每個單詞在文檔中的位置組成。
例如,我們有兩個文檔,每個文檔content字段包含:
- The quick brown fox jumped over the lazy dog
- Quick brown foxes leap over lazy dogs in summer
為了創建倒排索引,我們首先切分每個文檔的content字段為單獨的單詞(我們把它們叫做詞(terms)或者表征(tokens)),把所有的唯一詞放入列表並排序,結果是這個樣子的:
現在,如果我們想搜索"quick brown",我們只需要找到每個詞在哪個文檔中出現即可:
兩個文檔都匹配,但是第一個比第二個有更多的匹配項。 如果我們加入簡單的相似度算法(similarity algorithm),計算匹配單詞的數目,這樣我們就可以說第一個文檔比第二個匹配度更高——對於我們的查詢具有更多相關性。
但是在我們的倒排索引中還有些問題:
- "Quick"和"quick"被認為是不同的單詞,但是用戶可能認為它們是相同的。
- "fox"和"foxes"很相似,就像"dog"和"dogs"——它們都是同根詞。
- "jumped"和"leap"不是同根詞,但意思相似——它們是同義詞。
上面的索引中,搜索"+Quick +fox"不會匹配任何文檔(記住,前綴+表示單詞必須匹配到)。只有"Quick"和"fox"都在同一文檔中才可以匹配查詢,但是第一個文檔包含"quick fox"且第二個文檔包含"Quick foxes"。(就是單復數和同義詞沒法匹配,必須完全匹配。)
用戶可以合理地希望兩個文檔都能匹配查詢,我們也可以做得更好。如果我們將詞為統一為標准格式,這樣就可以找到不是確切匹配查詢,但是足以相似從而可以關聯的文檔。例如:
- "Quick"可以轉為小寫成為"quick"。
- "foxes"可以被轉為根形式"fox"。同理"dogs"可以被轉為"dog"。
- "jumped"和"leap"同義就可以只索引為單個詞"jump"
現在的索引:
但我們還未成功。我們的搜索"+Quick +fox"依舊失敗,因為"Quick"的確切值已經不在索引里,不過,如果我們使用相同的標准化規則處理查詢字符串的content字段,查詢將變成"+quick +fox",這樣就可以匹配到兩個文檔。
重要:我們只可以找到確實存在於索引中的詞,所以索引文本和查詢字符串都要標准化為相同的形式。而這個標記化和標准化的過程叫做分詞(analysis)。
分析和分析器
分析(analysis)是這樣一個過程:
- 首先,標記化一個文本塊為適用於倒排索引單獨的詞(term)。
- 然后標准化這些詞為標准形式,提高它們的“可搜索性”或“查全率”。
這個工作是分析器(analyzer)完成的。一個分析器(analyzer)包含三個功能:
字符過濾器
首先字符串經過字符過濾器(character filter),它們的工作是在標記化前處理字符串。字符過濾器能夠去除HTML標記,或者轉換"&"為"and"。
分詞器
下一步,分詞器(tokenizer)被標記化成獨立的詞。一個簡單的分詞器(tokenizer)可以根據空格或逗號將單詞分開(這個在中文中不適用)。
標記過濾
最后,每個詞都通過所有標記過濾(token filters),它可以修改詞(例如將"Quick"轉為小寫),去掉詞(例如停用詞像"a"、"and"、"the"等等),或者增加詞(例如同義詞像"jump"和"leap")。
Elasticsearch提供很多開箱即用的字符過濾器,分詞器和標記過濾器。這些可以組合來創建自定義的分析器以應對不同的需求。
內建的分析器
不過,Elasticsearch還附帶了一些預裝的分析器,你可以直接使用它們。下面我們列出了最重要的幾個分析器,來演示這個字符串分詞后的表現差異:
"Set the shape to semi-transparent by calling set_trans(5)"
標准分析器
標准分析器是Elasticsearch默認使用的分析器。對於文本分析,它對於任何語言都是最佳選擇(沒特殊需求,對於任何一個國家的語言,這個分析器就夠用了)。它根據Unicode Consortium的定義的單詞邊界(word boundaries)來切分文本,然后去掉大部分標點符號。最后,把所有詞轉為小寫。產生的結果為:
set, the, shape, to, semi, transparent, by, calling, set_trans, 5
Unicode:是為了解決傳統的字符編碼方案的局限而產生的,它為每種語言中的每個字符設定了統一並且唯一的二進制編碼,以滿足跨語言、跨平台進行文本轉換、處理的要求。
簡單分析器
簡單分析器將非單個字母的文本切分,然后把每個詞轉為小寫。產生的結果為:
set, the, shape, to, semi, transparent, by, calling, set, trans
空格分析器
空格分析器依據空格切分文本。它不轉換小寫。產生結果為:
Set, the, shape, to, semi-transparent, by, calling, set_trans(5)
語言分析器
特定語言分析器適用於很多語言。它們能夠考慮到特定語言的特性。例如,english
分析器自帶一套英語停用詞庫——像and或the這些與語義無關的通用詞。這些詞被移除后,因為語法規則的存在,提取出詞根,英語單詞的主體含義依舊能被理解。
english
分析器將會產生以下結果:
set, shape, semi, transpar, call, set_tran, 5
測試分析器
對於如何分詞以及存儲到索引中理解起來比較困難。為了更好的理解如何進行,可以使用analyze API來查看文本是如何被分析的。在查詢字符串參數中指定要使用的分析器,被分析的文本做為請求體:
GET /_analyze?analyzer=standard&text=Text to analyze
結果中每個節點在代表一個詞:
{
"tokens": [
{
"token": "text",
"start_offset": 0,
"end_offset": 4,
"type": "<ALPHANUM>",
"position": 1
},
{
"token": "to",
"start_offset": 5,
"end_offset": 7,
"type": "<ALPHANUM>",
"position": 2
},
{
"token": "analyze",
"start_offset": 8,
"end_offset": 15,
"type": "<ALPHANUM>",
"position": 3
}
]
}
token
是一個實際被存儲在索引中的詞。position
指明詞在原文本中是第幾個出現的。start_offset
和end_offset
表示詞在原文本中占據的位置。
當分析器被使用
當我們索引(index)一個文檔,全文字段會被分析為單獨的詞來創建倒排索引。不過,當我們在全文字段搜索(search)時,我們要讓查詢字符串經過同樣的分析流程處理,以確保這些詞在索引中存在。
- 當你查詢全文(full text)字段(被分析過的文本),查詢將使用相同的分析器來分析查詢字符串,以產生正確的詞列表。
- 當你查詢一個確切值(exact value)字段,查詢將不分析查詢字符串,但是你可以自己指定。
典型的例子
在索引中有12個tweets
,只有一個包含日期2014-09-15,但是我們看看下面查詢中的結果total。
GET /_search?q=2014 # 12 個結果
GET /_search?q=2014-09-15 # 還是 12 個結果 !
GET /_search?q=date:2014-09-15 # 1 一個結果
GET /_search?q=date:2014 # 0 個結果 !
為什么全日期的查詢返回所有的tweets,而針對date字段進行年度查詢卻什么都不返回? 為什么我們的結果因查詢_all
字段(默認所有字段中進行查詢)或date
字段而變得不同?(如果看不懂簡單查詢,可以查看ElasticSearch 5學習(4)——簡單搜索筆記)。
讓我們看看Elasticsearch在對gb
索引中的tweet
類型進行mapping
。
注意:還是一樣的,如果是Elasticsearch 5.0之前版本的text
將會是string
類型。
現在我們來看為什么會產生這樣的結果:
date
字段包含一個確切值:單獨的一個詞"2014-09-15"。_all
字段是一個全文字段,所以分析過程將日期轉為三個詞:"2014"、"09"和"15"。
當我們在_all
字段查詢2014,它一個匹配到12條推文,因為這些推文都包含詞2014。
當我們在_all
字段中查詢2014-09-15,首先分析查詢字符串,產生匹配任一詞2014、09或15的查詢語句,它依舊匹配12個推文,因為它們都包含詞2014。
當我們在date
字段中查詢2014-09-15,它查詢一個確切的日期,然后只找到一條推文。
當我們在date
字段中查詢2014,沒有找到文檔,因為沒有文檔包含那個確切的日期。
指定分析器
當Elasticsearch在你的文檔中探測到一個新的字符串字段,它將自動設置它為全文string
(棄用)字段並用standard
分析器分析。
你不可能總是想要這樣做。也許你想使用一個更適合這個數據的語言分析器。或者,你只想把字符串字段當作一個普通的字段——不做任何分析,只存儲確切值,就像字符串類型的用戶ID或者內部狀態字段或者標簽。
為了達到這種效果,我們必須通過映射(mapping)人工設置這些字段。
自定義字段映射
雖然大多數情況下基本數據類型已經能夠滿足,但你也會經常需要自定義一些特殊類型(fields),特別是字符串字段類型。 自定義類型可以使你完成一下幾點:
- 區分全文(full text)字符串字段和准確字符串字段(譯者注:就是分詞與不分詞,全文的一般要分詞,准確的就不需要分詞,比如『中國』這個詞。全文會分成『中』和『國』,但作為一個國家標識的時候我們是不需要分詞的,所以它就應該是一個准確的字符串字段)。
- 使用特定語言的分析器(例如中文、英文、阿拉伯語,不同文字的斷字、斷詞方式的差異)
- 優化部分匹配字段
- 等等
映射中最重要的字段參數是type
。除了string
(棄用)類型的字段,你可能很少需要映射其他的type
(因為一般情況下,Elasticsearch自動幫我們映射的類型都能滿足我們需求):
{
"number_of_clicks": {
"type": "integer"
}
}
string
(棄用)類型的字段,默認的,考慮到包含全文本,它們的值在索引前要經過分析器分析,並且在全文搜索此字段前要把查詢語句做分析處理。
對於string
(棄用)字段,兩個最重要的映射參數是index
和analyer
。
index
index參數控制字符串以何種方式被索引。它包含以下三個值當中的一個:
analyzed:首先分析這個字符串,然后索引。換言之,以全文形式索引此字段。
not_analyzed:索引這個字段,使之可以被搜索,但是索引內容和指定值一樣。不分析此字段。
no:不索引這個字段。這個字段不能為搜索到。
string
類型字段默認值是analyzed
。如果我們想映射字段為確切值,我們需要設置它為not_analyzed
:
{
"tag": {
"type": "string",
"index": "not_analyzed"
}
}
注意:其他簡單類型(long、double、date等等)也接受index參數,但相應的值只能是no
和not_analyzed
,它們的值不能被分析。
analyer
對於analyzed
類型的字符串字段,使用analyzer
參數來指定哪一種分析器將在搜索和索引的時候使用。默認的,Elasticsearch使用standard
分析器,但是你可以通過指定一個內建的分析器來更改它,例如whitespace
、simple
或english
。
{
"tweet": {
"type": "string",
"analyzer": "english"
}
}
更新映射
你可以在第一次創建索引的時候指定映射的類型。此外,你也可以晚些時候為新類型添加映射(或者為已有的類型更新映射)。
重要:你可以向已有映射中增加字段,但你不能修改它。如果一個字段在映射中已經存在,這可能意味着那個字段的數據已經被索引。如果你改變了字段映射,那已經被索引的數據將錯誤並且不能被正確的搜索到。
我們可以更新一個映射來增加一個新字段,但是不能把已有字段的類型那個從analyzed
改到not_analyzed
。
為了演示兩個指定的映射方法,讓我們首先刪除索引gb
:
DELETE /gb
然后創建一個新索引,指定tweet
字段的分析器為english
:
PUT /gb
{
"mappings": {
"tweet" : {
"properties" : {
"tweet" : {
"type" : "string",
"analyzer": "english"
},
"date" : {
"type" : "date"
},
"name" : {
"type" : "string"
},
"user_id" : {
"type" : "long"
}
}
}
}
}
再后來,我們決定在tweet
的映射中增加一個新的not_analyzed
類型的文本字段,叫做tag
,使用_mapping
后綴:
PUT /gb/_mapping/tweet
{
"properties" : {
"tag" : {
"type" : "string",
"index": "not_analyzed"
}
}
}
注意到我們不再需要列出所有的已經存在的字段,因為我們沒法修改他們。我們的新字段已經被合並至存在的那個映射中。
測試映射
你可以通過名字使用analyze
API測試字符串字段的映射。對比這兩個請求的輸出:
GET /gb/_analyze?field=tweet&text=Black-cats
GET /gb/_analyze?field=tag&text=Black-cats
tweet
字段產生兩個詞,"black"和"cat",tag
字段產生單獨的一個詞"Black-cats"。換言之,我們的映射工作正常。
Elasticsearch 5.0及之前
前面很多次提到string
在Elasticsearch 5.0開始被廢棄,取而代之的是text
和keyword
兩種類型,是不是很奇怪?不過如果你已經理解前面string
的兩種處理方式(全文和精確值),你就很快可以理解:
- text:類似於全文形式,包括被分析。
- keyword:屬於字符串類型的精確搜索。
除了將string分成兩個類型處理,其他主要的設置和使用基本相同,具體可以查看https://www.elastic.co/guide/en/elasticsearch/reference/5.0/string.html
轉載請注明出處。
作者:wuxiwei
出處:http://www.cnblogs.com/wxw16/p/6195284.html