ES中文分词器之精确短语匹配(解决了match_phrase匹配不全的问题)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ES中文分词器之精确短语匹配(解决了match_phrase匹配不全的问题)相关的知识,希望对你有一定的参考价值。

参考技术A

调研了几种分词器,例如IK分词器,ansj分词器,mmseg分词器,发现IK的分词效果最好。举个例子:

在上述例子中,IK和Mmsg 用的同一套词典。Ansj和IK,Mmsg使用的不是一套词典,也没有配置停词。

本文讲的中文分词器就是IK分词器。

楼主意淫着将所有的单字放入词典中,这样用ik_max_word 对数据建索引时既可以把词分出来建索引,又可以把字分出来建索引。然后用 ik_smart 将查找短语,因为ik_smart分出的数据是 ik_max_word 的一个子集,如果要查找的短语在原文中有出现,那么一定可以查到。后来发现用ik_smart分词器查找句子(match_phrase)时一个都没有查到,exo???为什么会查不到呢?明明是一个子集。对此官方网站对match_phrase的解释如下:

意思就是说用match_phrase查找时,查找分词器分出的词的位置和要建索引时分出的词的位置一样。举个例子:

从上面可以看出,查找时ik_smart将语句分为了快乐和感恩两个词,位置分别为1和2,而ik_max_word建索引时,快乐和感恩的位置分别是1和4,在match_phrase看来,这种是不匹配的,所以用ik_smart分词短语时无法查到或者查全数据。

好吧,既然ik_smart无法查到,我用ik_max_word查找总行了吧。用上述的例子,查找”快乐“时,你会发现你用ik_max_word查找到的结果没有standard分词器建索引查找获取到的结果多。原因和上述讲的一样:

在构建索引的时候,快乐,快和乐的位置分别是1,2,4,而查找时分词的顺序是1,2,3,然后match_phrase认为其不匹配,因此查询不到这种结果。

遇到问题了,在网上寻求解决方案。看了几篇博客,都指出了match_phrase的这个匹配问题,解决方案有以下两种:

standard分词器大家都比较熟,针对于汉字就是一个一个分,这种肯定是可以查全的。但一个一个字分的话,每个字对应的文档集合非常多,如果数据量达到了百亿,在求交集,计算距离时,效果非常差。

Ngram分词器类似于standard分词器,他可以指定分词的长度,然后用standard的方法切割。比如说“节日快乐”,我们指定切割的长度为2,NGram会切成“节日”,“日快”,“快乐”。虽然查找时可以减少每个token对应的文档数,但是存储量会增大很多,而且不在支持模糊的match匹配。很土。

ik_max_word构建索引,ik_smart无法查找,原因是ik_max_word分出了所有的词,ik_smart只分出了一种词,由于match_phrase本身的限制导致ik_smart查找不到。那我构建的时候采用ik_smart,查找的时候也用ik_smart,这样只要原文中有数据,构建和查找用同一种分词方法,就应该可以查找得到。测试后发现,这种也有很大的问题,即像“潜行者”这样的词,只分为了“潜行”和“者”两个token,但是“行者”也是一个词,在查找“行者”时无法查全数据。

ik_smart无法查全的原因是只分出了一种词的可能性,导致有些词查询不全。ik_max_word能解决这个问题。。但是ik_max_word的问题是如果查找的最后一个字能和原文中这个字的下一个字组成词语,那么就会出现无法查全的问题。我们能不能让ik_max_word将词和字分开?

当然可以,对一个属性指定两种分词方法:

这样ulluin属性采用standard分词,即单字分词,ulluin.ik采用ik_max_word即按词分词, ik_max_word的词典中去除所有的单字

查询时先将查询短语分词,如果第一个token和最后一个token中存在一个字,那么这个字可能与原文中的下一个字或者上一个字组成词,导致ik_max_word无法查到,这时我们用standard分词器在ulluin中查询,如果第一个token和最后一个token都是词,那么说明可以在ik_max_word中查询。来吧,测试一下:

为什么还是有问题?????ik_max_word查出的数据量比standard的少???还是因为match_phrase的限制,索引中“节日”和“快乐”的位置是1和3,而查找时“节日”和“快乐”的位置是1和2。这个问题很好解决,用match_phrase_prefix查询即可,即:

上面还提到ik_max_word有一个问题是分出的词语比standard的多,我们过滤了单字分词后,这个效果就会有很大的提升。假设我们的词典没有四字分词,只有二三字。比如说

可以看出,修改后的效果要比standard的效果好的多,不但token数变少了,而且每个token对应的文档数也大大的降低,减少了求交集的数据量和计算距离的数据量。

至此总算解决了ES中文分词切精确匹配的问题。

Es 模糊查询的方式

参考技术A Es 模糊查询的方式

要求:
Es查询:

查询工单信息, 输入 “测试”,查出 form_name 为字段中有查询出含有符合内容的数据

match:分词模糊查询:
比如“Everything will be OK, All is well”,会被分词一个一个单词(不是单个字母)

match_phrase :短语模糊查询
match_phrase是短语搜索,即它会将给定的短语(phrase)当成一个完整的查询条件。

比如查询 “Everything will”,会当成一个完整的短语进行查询, 会查出含有该查询条件的内容。

如果是查询单个字母,match就不管用了,那该如何处理呢?

wildcard:通配符模糊查询:

匹配任意字符

匹配0个或多个字符

记录是存在的,但是没有查出来? 因为分词的影响,添加keyword 进行处理

Wildcard 性能会比较慢。如果非必要,尽量避免在开头加通配符 ? 或者 *,这样会明显降低查询性能

如果查询的内容非空,怎么处理? 直接用*

总结:
Es 模糊查询, 分词的用match; 短语的用match_phrase;查询任意的,用wildcard通配符,注意查询的内容是否分词,分词的添加keyword,查询非空的情况,用*。

以上是关于ES中文分词器之精确短语匹配(解决了match_phrase匹配不全的问题)的主要内容,如果未能解决你的问题,请参考以下文章

Es 模糊查询的方式

关于Elasticsearch 使用 MatchPhrase搜索的一些坑

Spring Boot 2.0 整合 ES 5 文章内容搜索实战

es对中文进行精确查询

ES几种查询方式

NLTK RegexpParser,通过精确匹配一项来分块短语