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
- 首先在原來的分詞器基礎上新增
類型的分詞器和過濾器,確定不會影響到之前的拼音分詞的功能。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;
}
-
的分詞器中使用上面新增的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