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 中的一个方法,把中文字符串转换为拼音,获得一个字符串列表

List<String> pinyinList = Pinyin.pinyin(source);

我们在这个基础上新增了一个 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

你可能感兴趣的:(Elastic)