ElasticSearch 5學習(9)——映射和分析(string類型廢棄)


在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字段包含:

  1. The quick brown fox jumped over the lazy dog
  2. Quick brown foxes leap over lazy dogs in summer

為了創建倒排索引,我們首先切分每個文檔的content字段為單獨的單詞(我們把它們叫做詞(terms)或者表征(tokens)),把所有的唯一詞放入列表並排序,結果是這個樣子的:

現在,如果我們想搜索"quick brown",我們只需要找到每個詞在哪個文檔中出現即可:

兩個文檔都匹配,但是第一個比第二個有更多的匹配項。 如果我們加入簡單的相似度算法(similarity algorithm),計算匹配單詞的數目,這樣我們就可以說第一個文檔比第二個匹配度更高——對於我們的查詢具有更多相關性。

但是在我們的倒排索引中還有些問題:

  1. "Quick"和"quick"被認為是不同的單詞,但是用戶可能認為它們是相同的。
  2. "fox"和"foxes"很相似,就像"dog"和"dogs"——它們都是同根詞。
  3. "jumped"和"leap"不是同根詞,但意思相似——它們是同義詞。

上面的索引中,搜索"+Quick +fox"不會匹配任何文檔(記住,前綴+表示單詞必須匹配到)。只有"Quick"和"fox"都在同一文檔中才可以匹配查詢,但是第一個文檔包含"quick fox"且第二個文檔包含"Quick foxes"。(就是單復數和同義詞沒法匹配,必須完全匹配。)

用戶可以合理地希望兩個文檔都能匹配查詢,我們也可以做得更好。如果我們將詞為統一為標准格式,這樣就可以找到不是確切匹配查詢,但是足以相似從而可以關聯的文檔。例如:

  1. "Quick"可以轉為小寫成為"quick"。
  2. "foxes"可以被轉為根形式"fox"。同理"dogs"可以被轉為"dog"。
  3. "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_offsetend_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(棄用)字段,兩個最重要的映射參數是indexanalyer

index

index參數控制字符串以何種方式被索引。它包含以下三個值當中的一個:

analyzed:首先分析這個字符串,然后索引。換言之,以全文形式索引此字段。

not_analyzed:索引這個字段,使之可以被搜索,但是索引內容和指定值一樣。不分析此字段。

no:不索引這個字段。這個字段不能為搜索到。

string類型字段默認值是analyzed。如果我們想映射字段為確切值,我們需要設置它為not_analyzed

{
    "tag": {
        "type":     "string",
        "index":    "not_analyzed"
    }
}

注意:其他簡單類型(long、double、date等等)也接受index參數,但相應的值只能是nonot_analyzed,它們的值不能被分析。

analyer

對於analyzed類型的字符串字段,使用analyzer參數來指定哪一種分析器將在搜索和索引的時候使用。默認的,Elasticsearch使用standard分析器,但是你可以通過指定一個內建的分析器來更改它,例如whitespacesimpleenglish

{
    "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開始被廢棄,取而代之的是textkeyword兩種類型,是不是很奇怪?不過如果你已經理解前面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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM