ElasticSearch7.3學習(十五)----中文分詞器(IK Analyzer)及自定義詞庫

|舊市拾荒|發表於2022-03-28

1、 中文分詞器

1.1 預設分詞器

先來看看ElasticSearch中預設的standard 分詞器,對英文比較友好,但是對於中文來說就是按照字元拆分,不是那麼友好。

GET /_analyze
{
  "analyzer": "standard",
  "text": "中華人民共和國"
}

我們想要的效果是什麼:“中華人民共和國”作為一整個詞語。

得到的結果是:

{
  "tokens" : [
    {
      "token" : "中",
      "start_offset" : 0,
      "end_offset" : 1,
      "type" : "<IDEOGRAPHIC>",
      "position" : 0
    },
    {
      "token" : "華",
      "start_offset" : 1,
      "end_offset" : 2,
      "type" : "<IDEOGRAPHIC>",
      "position" : 1
    },
    {
      "token" : "人",
      "start_offset" : 2,
      "end_offset" : 3,
      "type" : "<IDEOGRAPHIC>",
      "position" : 2
    },
    {
      "token" : "民",
      "start_offset" : 3,
      "end_offset" : 4,
      "type" : "<IDEOGRAPHIC>",
      "position" : 3
    },
    {
      "token" : "共",
      "start_offset" : 4,
      "end_offset" : 5,
      "type" : "<IDEOGRAPHIC>",
      "position" : 4
    },
    {
      "token" : "和",
      "start_offset" : 5,
      "end_offset" : 6,
      "type" : "<IDEOGRAPHIC>",
      "position" : 5
    },
    {
      "token" : "國",
      "start_offset" : 6,
      "end_offset" : 7,
      "type" : "<IDEOGRAPHIC>",
      "position" : 6
    }
  ]
}

得到的結果不如人意,IK分詞器就是目前最流行的es中文分詞器

1.2 安裝ik分詞器

安裝我就不詳細說了,教程很多。

1.3 ik分詞器基礎知識

ik_max_word: 會將文字做最細粒度的拆分,比如會將“中華人民共和國人民大會堂”拆分為“中華人民共和國,中華人民,中華,華人,人民共和國,人民大會堂,人民大會,大會堂”,會窮盡各種可能的組合;

GET /_analyze
{
  "analyzer": "ik_max_word",
  "text": "中華人民共和國人民大會堂"
}
{
  "tokens" : [
    {
      "token" : "中華人民共和國",
      "start_offset" : 0,
      "end_offset" : 7,
      "type" : "CN_WORD",
      "position" : 0
    },
    {
      "token" : "中華人民",
      "start_offset" : 0,
      "end_offset" : 4,
      "type" : "CN_WORD",
      "position" : 1
    },
    {
      "token" : "中華",
      "start_offset" : 0,
      "end_offset" : 2,
      "type" : "CN_WORD",
      "position" : 2
    },
    {
      "token" : "華人",
      "start_offset" : 1,
      "end_offset" : 3,
      "type" : "CN_WORD",
      "position" : 3
    },
    {
      "token" : "人民共和國",
      "start_offset" : 2,
      "end_offset" : 7,
      "type" : "CN_WORD",
      "position" : 4
    },
    {
      "token" : "人民",
      "start_offset" : 2,
      "end_offset" : 4,
      "type" : "CN_WORD",
      "position" : 5
    },
    {
      "token" : "共和國",
      "start_offset" : 4,
      "end_offset" : 7,
      "type" : "CN_WORD",
      "position" : 6
    },
    {
      "token" : "共和",
      "start_offset" : 4,
      "end_offset" : 6,
      "type" : "CN_WORD",
      "position" : 7
    },
    {
      "token" : "國人",
      "start_offset" : 6,
      "end_offset" : 8,
      "type" : "CN_WORD",
      "position" : 8
    },
    {
      "token" : "人民大會堂",
      "start_offset" : 7,
      "end_offset" : 12,
      "type" : "CN_WORD",
      "position" : 9
    },
    {
      "token" : "人民大會",
      "start_offset" : 7,
      "end_offset" : 11,
      "type" : "CN_WORD",
      "position" : 10
    },
    {
      "token" : "人民",
      "start_offset" : 7,
      "end_offset" : 9,
      "type" : "CN_WORD",
      "position" : 11
    },
    {
      "token" : "大會堂",
      "start_offset" : 9,
      "end_offset" : 12,
      "type" : "CN_WORD",
      "position" : 12
    },
    {
      "token" : "大會",
      "start_offset" : 9,
      "end_offset" : 11,
      "type" : "CN_WORD",
      "position" : 13
    },
    {
      "token" : "會堂",
      "start_offset" : 10,
      "end_offset" : 12,
      "type" : "CN_WORD",
      "position" : 14
    }
  ]
}

ik_smart: 會做最粗粒度的拆分,比如會將“中華人民共和國人民大會堂”拆分為“中華人民共和國,人民大會堂”。

GET /_analyze
{
  "analyzer": "ik_smart",
  "text": "中華人民共和國人民大會堂"
}
{
  "tokens" : [
    {
      "token" : "中華人民共和國",
      "start_offset" : 0,
      "end_offset" : 7,
      "type" : "CN_WORD",
      "position" : 0
    },
    {
      "token" : "人民大會堂",
      "start_offset" : 7,
      "end_offset" : 12,
      "type" : "CN_WORD",
      "position" : 1
    }
  ]
}

1.4 ik分詞器的使用

儲存時,使用ik_max_word,搜尋時,使用ik_smart,原因也很容易想到:儲存時,儘量儲存多的可能性,搜尋時做粗粒度的拆分

例如,建立以下對映

PUT /my_index 
{
  "mappings": {
      "properties": {
        "text": {
          "type": "text",
          "analyzer": "ik_max_word",
          "search_analyzer": "ik_smart"
        }
      }
  }
}

2、ik配置檔案

ik配置檔案地址:外掛的config目錄下

ElasticSearch7.3學習(十五)----中文分詞器(IK Analyzer)及自定義詞庫

部分檔案內容如下:

  • IKAnalyzer.cfg.xml:用來配置自定義詞庫
  • main.dic:ik原生內建的中文詞庫,總共有27萬多條,只要是這些單詞,都會被分在一起,都會按照這個裡面的詞語去分詞,ik原生最重要的兩個配置檔案之一
  • preposition.dic: 介詞
  • quantifier.dic:放了一些單位相關的詞,量詞
  • suffix.dic:放了一些字尾
  • surname.dic:中國的姓氏
  • stopword.dic:包含了英文的停用詞,a the and at but等。會在分詞的時候,直接被幹掉,不會建立在倒排索引中。ik原生最重要的兩個配置檔案之一

3、自定義詞庫

3.1 自定義分詞詞庫

每年都會湧現一些特殊的流行詞,內卷,耗子尾汁,不講武德等,這些詞一般不會出現在ik的原生詞典裡,分詞的時候也不會把這些詞彙當作整個詞彙來進行分詞。所以需要我們自己補充自己的最新的詞語,到ik的詞庫裡面。

就拿耗子尾汁來說,不做自定義分詞的效果如下。

ElasticSearch7.3學習(十五)----中文分詞器(IK Analyzer)及自定義詞庫

在實際的搜尋過程中,肯定不希望把它分詞,而是希望把它作為一個整體的詞彙。

(1)首先在IK外掛的config目錄下,有一個IKAnalyzer.cfg.xml檔案。

ElasticSearch7.3學習(十五)----中文分詞器(IK Analyzer)及自定義詞庫

(2)使用Notepad++開啟該檔案

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
	<comment>IK Analyzer 擴充套件配置</comment>
	<!--使用者可以在這裡配置自己的擴充套件字典 -->
	<entry key="ext_dict"></entry>
	 <!--使用者可以在這裡配置自己的擴充套件停止詞字典-->
	<entry key="ext_stopwords"></entry>
	<!--使用者可以在這裡配置遠端擴充套件字典 -->
	<!-- <entry key="remote_ext_dict">words_location</entry> -->
	<!--使用者可以在這裡配置遠端擴充套件停止詞字典-->
	<!-- <entry key="remote_ext_stopwords">words_location</entry> -->
</properties>

(3)可以看到上面的提示

ElasticSearch7.3學習(十五)----中文分詞器(IK Analyzer)及自定義詞庫

(4)於是我們建立一個名為mydict.dic的檔案,內容如下

ElasticSearch7.3學習(十五)----中文分詞器(IK Analyzer)及自定義詞庫

(5)注意如果多個詞語,就著下一行接著錄入,然後把這個檔案放在與配置檔案的相同目錄下。

ElasticSearch7.3學習(十五)----中文分詞器(IK Analyzer)及自定義詞庫

(6)然後再把檔名mydict.dic新增在IKAnalyzer.cfg.xml檔案中,然後儲存

ElasticSearch7.3學習(十五)----中文分詞器(IK Analyzer)及自定義詞庫

(7)然後重啟es,檢視效果

ElasticSearch7.3學習(十五)----中文分詞器(IK Analyzer)及自定義詞庫

(9)可以看到,耗子尾汁這個詞已經能夠作為一個整體的詞語來做分詞了。

3.2 自定義停用詞庫

比如了,的,啥,麼,我們可能並不想去建立索引,讓人家搜尋。

做法與上面自定義詞庫類似,這裡只是簡單的說一下,比方說建立一個mystop.dic檔案,把不想建立的索引的詞寫進檔案,把檔案與配置檔案放在同一個目錄,然後在把檔名寫進配置檔案對應的位置,如下所示

ElasticSearch7.3學習(十五)----中文分詞器(IK Analyzer)及自定義詞庫

然後在重啟es,就可以檢視效果了。

這樣做的一個好處就是,已經有了常用的中文停用詞,但是可以補充自己的停用詞。

4、熱更新詞庫

4.1 熱更新

每次都是在es的擴充套件詞典中,手動新增新詞語,很坑

(1)每次新增完,都要重啟es才能生效,非常麻煩

(2)es是分散式的,可能有數百個節點,你不能每次都一個一個節點上面去修改

所以引出熱更新的解決方案。es不停機,直接我們在外部某個地方新增新的詞語,es中立即熱載入到這些新詞語

熱更新的方案

(1)基於ik分詞器原生支援的熱更新方案,部署一個web伺服器,提供一個http介面,通過modified和tag兩個http響應頭,來提供詞語的熱更新,這種方式在官網也提到過。https://github.com/medcl/elasticsearch-analysis-ik

    ElasticSearch7.3學習(十五)----中文分詞器(IK Analyzer)及自定義詞庫
修改了外掛配置之後需要重啟,如果之後對遠端的詞庫.txt檔案修改就不需要再重啟ES了,該外掛支援熱更新分詞。

(2)修改ik分詞器原始碼,然後手動支援從資料庫中每隔一定時間,自動載入新的詞庫

一般來說採用第二種方案,第一種,ik git社群官方都不建議採用,覺得不太穩定

4.2 步驟

1、下載原始碼,https://github.com/medcl/elasticsearch-analysis-ik/releases

ik分詞器,是個標準的java maven工程,直接匯入idea就可以看到原始碼

2、修改源

org.wltea.analyzer.dic.Dictionary類,160行Dictionary單例類的初始化方法,在這裡需要建立一個我們自定義的執行緒,並且啟動它

org.wltea.analyzer.dic.HotDictReloadThread類:就是死迴圈,不斷呼叫Dictionary.getSingleton().reLoadMainDict(),去重新載入詞典

Dictionary類,399行:this.loadMySQLExtDict(); 載入mysql字典。

Dictionary類,609行:this.loadMySQLStopwordDict();載入mysql停用詞

config下jdbc-reload.properties。mysql配置檔案

3、mvn package打包程式碼

target\releases\elasticsearch-analysis-ik-7.3.0.zip

4、解壓縮ik壓縮包

將mysql驅動jar,放入ik的目錄下

5、修改jdbc相關配置

6、重啟es

觀察日誌,日誌中就會顯示我們列印的那些東西,比如載入了什麼配置,載入了什麼詞語,什麼停用詞

7、在mysql中新增詞庫與停用詞

8、分詞實驗,驗證熱更新生效

這裡只是大概的一個步驟,具體情況按照自己的業務邏輯進行開發。

相關文章