Elasticsearch Analysis

Analyzer

Elasticsearch 利用 Analysis 模組來解析 Index 以及搜尋時輸入的字串,你可以在定義 mapping 的時候或透過特定 API 設定要使用的 analyzer。

一個 Analyzer 是由一到數個 Tokenizer、零到多個 Char FilterToken Filter 組成。而 AnalyzerTokenizer 以及 Token Filter 都可以在定義 mapping 時進行設定。

index :  
    analysis :
        analyzer :
            standard :
                type : standard
                stopwords : [stop1, stop2]
            myAnalyzer1 :
                type : standard
                stopwords : [stop1, stop2, stop3]
                max_token_length : 500
            # configure a custom analyzer which is
            # exactly like the default standard analyzer
            myAnalyzer2 :
                tokenizer : standard
                filter : [standard, lowercase, stop]
        tokenizer :
            myTokenizer1 :
                type : standard
                max_token_length : 900
            myTokenizer2 :
                type : keyword
                buffer_size : 512
        filter :
            myTokenFilter1 :
                type : stop
                stopwords : [stop1, stop2, stop3, stop4]
            myTokenFilter2 :
                type : length
                min : 0
                max : 2000

如果沒有特別指定的話,Elasticsearch 就會使用內建預設的 AnalyzerTokenizer 以及 Token Filter

Elasticsearch 也有提供 API 讓你直接測試不同文字被不同 analyzer 處理的結果,你可以用下面的指令去進行測試

# text 參數是要解析的字串, analyzer 則是你要用的 analyzer 的名稱
$ curl "localhost:9200/_analyze?text=The%20Quick%20Brown%20Foxes&analyzer=standard&pretty=true"

結果應該會像:

{
  "tokens" : [ {
    "token" : "the",
    "start_offset" : 0,
    "end_offset" : 3,
    "type" : "<ALPHANUM>",
    "position" : 1
  }, {
    "token" : "quick",
    "start_offset" : 4,
    "end_offset" : 9,
    "type" : "<ALPHANUM>",
    "position" : 2
  }, {
    "token" : "brown",
    "start_offset" : 10,
    "end_offset" : 15,
    "type" : "<ALPHANUM>",
    "position" : 3
  }, {
    "token" : "foxes",
    "start_offset" : 16,
    "end_offset" : 21,
    "type" : "<ALPHANUM>",
    "position" : 4
  } ]
}

而 Analysis 實際的運作如下圖,不管是 Index 資料或是搜尋時都會經過一樣的流程:字串會先照 Char Filter 的設定替換字元,接著依照 Tokenizer 定義的規則把字串拆成一個個 Token,最後再由 Token Filter 來過濾或處理特定 token

Char Filter

首先介紹 Char Filter ,它的作用是依照你定義的規則,替換字串內的字元(有點像 Ruby 的 gsub),最常用於過濾 HTML 語法,或是把特殊符號轉成有意義的單字,像是把 & 轉成 and。假設我們定義了一個 my_char_filter,他的規則為將任何 Q 轉換成 K,字串就會在一開始時把 Quick 變成 Kuick,然後繼續傳下去給 Tokenizer

Tokenizer

再來是 Tokenizer,他跟 Compiler 會用到的 Tokenizer 功能差不多,用來把字串拆成數個 Token,每個 Token 都還會帶有一些特別的屬性以供後續處理,看起來會像是:

 {
    "token" : "the",       # Token 的字串
    "start_offset" : 0,    # 在字串中的起始位置
    "end_offset" : 3,      # 在字串中的結束位置
    "type" : "<ALPHANUM>", # 文字的類別,他會去辨別這個 Token 是什麼語言的文字
    "position" : 1         # 標明為這個字串中的第幾個 Token
  }

接下來我們可以用下圖來看不同 Tokenizer 會產出的不同結果。假設我們有一串文字 My mail is [email protected],然後分別用 standard 以及 classic Tokenizer 來做分詞

由於 Classic 會直接跳過 stop word(像是 isand 之類的詞,這邊有英文 stop word 的列表),而且不會視 @ 為分段點,所以最後的 Token 會是 my, mail[email protected];而預設的 Standard 則會依據空白以及特殊字元分詞,所以最後的結果會是 my, mail, is, stan001212gmail.com

從這邊也可以看到在 Analysis 過程中,對結果裡面影響最大的部分就是 Tokenizer 的選擇。如果在索引時使用 standard Tokenizer,而搜尋時使用 classic,結果會有很大的差異,甚至會讓使用者沒辦法用當初儲存的字串搜尋到同樣的結果,所以在選擇及設定 Analyzer 時,首先要注意的就是他使用的 Tokenizer 分詞規則是否相近,以免產生嚴重誤差的情形

Token Filter

最後介紹的是 Token Filter,他可以更改 Token 的內容(像是換成小寫字母)、刪除特定 Token(例如移除 Stop Word)以及增加 Token(加入相似字)。像前面的例子中都有用到 lowercase 這個 filter 來讓 token 一致的變成小寫字母;又或是可以像下圖一樣,使用 english_stop 來濾掉 stop word

從圖片中可以看到,只要妥善利用 Token Filter,可以讓使用者不會因為一些無關內容的差異而影響他的搜尋結果,提高搜尋的彈性。

總結

Elasticsearch 的 Analysis 模組會是影響搜尋準確度的關鍵因素,不過很可惜的是目前的 Analyser 多半不支援中文,要對中文進行斷詞的話必須使用額外的插件,例如 IK。而每個 Analyzer 又可以針對 Char FilterTokenizer 以及 Token Filter 進行調整甚至自訂,所以這部分是很值得花時間心力下去研究的。

Author

Stan Luo

I'm Stan, a junior web developer. I love writing ruby program and rails application.And I'm also looking forward making some contribution the ruby community.

Posted this article on elasticsearch, analysis
comments powered by Disqus