天天看點

ElasticSearch pinyin分詞支援多音字ElasticSearch pinyin分詞支援多音字

ElasticSearch pinyin分詞支援多音字

背景

我們有一個搜尋業務底層采用ElasticSearch作為搜尋引擎,在索引的過程中,使用了ik中文分詞、拼音分詞、同義詞等多種分詞器。ES和各種插件的組合已經能滿足我們線上90%的需求,但是仍有部分需求無法覆寫,我們在拼音分詞的時候過程中就遇到了無法解決的問題。

比如在

三一重工

中,

一重

這個詞在拼音詞庫

polyphone.txt

中有對應的詞彙,讀作

yichong

,是以這整個詞的讀音為

sanyichonggong

, 但是其真實讀音應該為

sanyizhonggong

。這是因為在拼音分詞過程中會先去詞庫中檢索是否有對應的詞彙,沒有的話再用單字拼音代替最後拼接在一起。

再比如在

蔚來汽車

中,

蔚來

算是一個新詞,在拼音詞庫

polyphone.txt

中沒有對應的詞彙,是以這個詞對應的拼音是每個字的拼音拼接而成,結果為

yulai

。但是其真實讀音應該為

weilai

, 那麼我們的使用者就無法通過拼音

weilai

搜尋到相關的内容。

經過檢視拼音分詞源代碼發現,拼音分詞其實是調用nlp-lang這個項目裡的方法實作的分詞器。而這個nlp-lang項目中,拼音解析如果遇到多音字僅僅傳回第一個拼音,這樣很多讀音都無法擷取到。

if(temp.length()==1){
    //單個字取第一個拼音
    lists.add(PinyinFormatter.formatPinyin(word.getParam()[0], format));
} else {
    for (String t : word.getParam()) {
        lists.add(PinyinFormatter.formatPinyin(t, format));
    }
}
           

面對龐大的多音字清單,通過手工維護、修改詞彙清單顯然無法完全達到目的。

為此,我們決定調整這部分代碼滿足我們線上業務的需求。

調整

這部分僅僅介紹調整思路,不設計具體代碼實作。

1. nlp-lang

拼音分詞會調用 nlp-lang 中的一個方法,把中文字元串轉換為拼音,獲得一個字元串清單

我們在這個基礎上新增了一個

multiplePinyin

方法,可以擷取多音字所有讀音,并且不再檢索

polyphone.txt

中的詞庫對照表。

System.out.println(Pinyin.pinyin("蔚來"))

>>> ['yu', 'lai']

System.out.println(Pinyin.multiplePinyin("蔚來"))

>>> ['yu wei', 'lai']

System.out.println(Pinyin.pinyin("三一重工"))

>>> ['san', 'yi', 'chong', 'gong']

System.out.println(Pinyin.multiplePinyin("三一重工"))

>>> ['san', 'yi', 'zhong chong', 'gong']
           

多音字的多個讀音用空格分割。

2. elasticsearch-analysis-pinyin

  1. 首先在原來的分詞器基礎上新增

    multiple_pinyin

    類型的分詞器和過濾器,確定不會影響到之前的拼音分詞的功能。
public Map<String, AnalysisModule.AnalysisProvider<org.elasticsearch.index.analysis.TokenFilterFactory>> getTokenFilters() {
    Map<String, AnalysisModule.AnalysisProvider<org.elasticsearch.index.analysis.TokenFilterFactory>> extra = new HashMap<>();
    extra.put("pinyin", PinyinTokenFilterFactory::new);
    
    // 新增加的分詞類型
    extra.put("multiple_pinyin", MultiplePinyinTokenFilterFactory::new);
    return extra;
}
           
  1. multiple_pinyin

    的分詞器中使用上面新增的

    Pinyin.multiplePinyin

    方法擷取到每個字的多音字。然後根據空格拆分後将所有可能的結果組合在一起。
// pinyin  "蔚來"

["yulai"]

// multiple_pinyin  "蔚來"

["yulai", "weilai"]


// pinyin "三一重工"

["sanyichonggong"]

// multiple_pinyin  "三一重工"

["sanyizhonggong", "sanyichonggong"]

// pinyin  "廈門重工" (兩個多音字:夏、重)

["xiamenzhonggong"]

// multiple_pinyin  "廈門重工"

["shamenzhonggong", "shamenchonggong", "xiamenzhonggong", "xiamenchonggong"]
           

問題

因為支援多音字的拼音分詞是所有讀音可能結果的笛卡爾積,是以當輸入的字元串長度過大時,分詞的結果可能會特别大。假如輸入的字元串中有10個字是多音字,每個字都有2種讀音,那麼分詞結果就有

2^10個

。可想而之,耗時會非常長。

我們的使用場景中,僅僅針對物品名稱進行分詞,名稱不會很長,暫時沒有遇到性能瓶頸。

相關代碼

nlp-lang

elasticsearch-analysis-pinyin

繼續閱讀