基于Lucene的联系人拼音检索(第三部分:遗留问题解决)
遗留问题
前两篇文章中提到了检索的时候不支持的一些特性,比如孙燕姿这个名字输入syanz无法查找,为了解决这个问题,今天又对切词的部分进行了修正。
解决
对于名字字段进行两种全拼和简拼转换,比如孙燕姿,拼音转换后的结果是sunyanzi,syz,然后再对这个转换结果进行前向后向两个方向的N-Gram切分。
原有的设计是拼音转换有四种,对于“沈世卿”这样的名字,拼音转换结果有shenshiqing,shshq, ssq, shensq,通过这个方式枚举所有组合编码上不漂亮,设计起来也不nice。所以放弃了这种方式。
同时增加了了一个类似于SpellCheck的字段,这个是参考自:http://lucene.apache.org/java/3_0_1/api/contrib-spellchecker/org/apache/lucene/search/spell/SpellChecker.html
这个字段也只进行全拼和简拼两种拼音转换,但转换后的结果使用了标准的N-Gram而非EdgeNGram的TokenFilter,这样这个字段就可以进行拼写检查的工作。
你可能会问了,为什么不只使用拼写检查一个字段来进行搜索?
- 一个是我们要对结果进行高亮,如果这样存储过大,并且还要存储位置信息,存储就更大了,没有提到的是拼写检查字段我是不存储的,只是分词和索引。
- 第二个问题是,如果只使用拼写检查一个字段,检索的时候QueryParser上也会遇到问题,如果选择AND_OPERATOR,那么输入错误就检索不到结果,如果使用OR_OPERATOR,那么搜索结果太多无关的,用户会觉得很奇怪。否则就需要检索的时候手动做一次N-Gram的切分,然后在使用这个切分后的组合(中间加空格)来查询。
这样设计好了之后如何检索:
检索时候的分词我们使用WhiteSpaceAnalyzer+ICU+Lowercase作为QueryParser的analyzer(实际上我使用的MultipleFieldQueryParser+PerFieldAnalyzer,不过原理上类似,考虑下就清楚了),同时使用AND_OPERATOR
如果第一次检索结果为空,那么我们使用拼写检查字段字段进行检索,QueryParser使用索引时候使用的Analyzer,同时使用OR_OPERATOR,在使用这个新的QueryParser检索一次,将前两个结果(如果有的话)拼接成新的检索关键词再检索一次,这次的结果返回给用户,如果拼写检查返回空,那么仍然使用原始关键词检索一次返回给用户。不要担心多次检索的性能损失,这个耗时很少,要比你整理结果返回给用户的耗时还少,所以大可不比担心。这个过程对用户透明,用户感觉不到这个过程,经过这样的改进,我发现对于用户的任意输入能尽可能的给出满意的结果,减少了空结果的返回可能性,同时允许了用户更加随意的输入,同时由于减少了拼音转换的次数,性能也有了细微的提升(目前还没时间做压力,压力估计要这个月底才能进行)。
同时还进行了简单的优化,假设拼写检查返回了两个结果(我是设置了按照相似度得分降序),那么前两个结果应该有不同的权重,所以我也对这两个搜索词赋予了逐级下降的权重,那么检索的结果会更优。