Elasticsearch 中文同義詞與自動完成

wh chi
CW-ITGROUP
Published in
6 min readMay 2, 2020
康健知識庫自動完成示意圖,Elasticsearch 版本 7.6.2

最近幫公司實作自動完成的功能,趁機補了一下相關的基礎知識,寫在這邊跟各位分享,這篇文章提供要使用 Elasticsearch 完成中文同義詞與自動完成所需的最小知識。

專有名詞

右側是 RDBMS 的名詞,用類比的方式比較有帶入感

其中有幾個概念跟這篇文章比較有關

  • document

可以被 index 的最小單位。

  • settings

針對某個 index 的 tokenizer、filter、shards、replica…等的設定,可以 update,要先關閉後才能做。

  • mappings

定義某個index包含的fields的儲存方式,無法 update,有分靜態和動態,動態是指當有新 field 產生時 Elasticsearch 會嘗試猜測並賦予類型,只支援這幾種類型

額外要提的是 type

在 7.0 版本之後預設移除,8.0 則不支援,官方說明原因為其設計理念與 Luence 實際儲存資料的方式有衝突:在 RDBMS 的 context 中,table 之間的 column 不會互相影響,而在 Elasticsearch 對 filed 儲存方式是在同個 index 裡面同名稱的 field 吃同樣的 mapping,跟原本想做到的概念相違。

index概念簡介

Inverted Index 也是經過 Analyzer 之後才走

doc 透過 analyzer 處理後儲存成類似圖中的表,搜尋時用 Inverted index 的方法找出其於表中出現的次數,再去推出要找的doc,如圖中輸入”somethin funny” 查找後發現 “something”跟”funny”都有出現在 doc1 中,因此回傳 doc1 作為搜尋結果。

analyzer概念簡介

Character Filter + Tokenizer + Token Filter = Analyzer

analyzer 由三個部分組成由處理順序左到右如圖,只有 text field 才支援analyzer 設定。圖解如下:

輸入字串透過 character filter 把字串的 html tag 去除後經過 whitespace tokenizer 去掉空白字元,最後經過 synonym filter 把同義字替換後再存到Inverted index 中準備提供查詢。

有了 analyzer 的概念後在設定同義詞時會比較明白在設定的到底是什麼。

同義詞

這邊使用 ik 分詞器,比較老牌(出問題比較能找到解)的中文分詞器,搭配自定義的字典檔以及同義字字庫就能開始使用,在 analyzer 中是屬於 Tokenizer 的層級。

  • 範例

字典檔(custom-dict.txt):讓進去的字不要被切斷,能夠正確被同義字字庫比對。

胃食道逆流
流行性感冒

同義字字庫(synonym.txt)

流感,流行性感冒
胃食道逆流=>胃病

Dockerfile

FROM elasticsearch:7.6.2RUN ./bin/elasticsearch-plugin install -b https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.6.2/elasticsearch-analysis-ik-7.6.2.zip
RUN mkdir -p /usr/share/elasticsearch/config/analysis-ik/custom
COPY ./dict/custom-dict.txt /usr/share/elasticsearch/config/analysis-ik/custom/custom-dict.dic
COPY ./dict/synonym.txt /usr/share/elasticsearch/config/analysis/synonym.txt
USER elasticsearch

利用上面這些範例可快速在本地建立一個具有 ik 分詞功能的 elasticsearch,接著在設定 mapping 與 setting

PUT /<index>

設定成功的話測試結果如下

POST /<index>/_analyze

自動完成

官方推薦兩種方式

  • Completion Suggester

回應速度很快,但其資料結構因素只能做到prefix completion。

  • search-as-you-type (7.2~)

使用 shingle token filter (ngram)當作 base,因此可以做到 infix completion。舊的版本可以用 ngram token filter 達到類似效果。

我最後使用 search-as-you-type,因為這個效果比較符合使用情境,設定步驟如下:

建立 mapping

PUT /<index>

建立 data

PUT /<index>/<id>

測試結果

GET /<index>/_search

前端隨便找一套看的順眼的 auto-completion library 套一下就好~這邊延用之前同義字的 analyzer,因為這樣自動完成出來的東西才不會被切的很奇怪。

上面大概說明怎麼快速建立一個具有同義字和自動完成功能的 Elasticsearch node,至於效果好不好得看字庫範圍夠不夠集中且多元,這塊很吃領域知識,這篇文章提供了「有」的部分,要「好」的話還有更多需要做的。

--

--

wh chi
CW-ITGROUP

創作在完成的那一刻就獨立於創作者之外,程式碼也是