Lucene4如何在无通配符前提下实现复合词子词检索
实现方案说明
你之前使用ShingleFilter未达到预期的核心原因是:ShingleFilter的作用是合并分词器输出的多个相邻Token生成连续词组,你搭配KeywordTokenizer会将整个输入词作为单个Token输入,长度为2的Shingle配置不会对单个Token做拆分,自然无法生成子词。
如果不需要给检索词加通配符,最优方案是通过NGram系列分词组件在索引阶段拆分所有子词存入倒排索引,检索时直接匹配子词即可。
1. 仅前缀匹配场景(例:搜auto命中automobile,搜mobile不命中)
适合大部分前缀检索需求,占用存储空间更小,使用EdgeNGramFilter实现,自定义分析器代码如下:
public class PrefixSubwordAnalyzer : Analyzer { private readonly LuceneVersion _version; // 子词最小/最大长度,可根据业务调整 private readonly int _minGram = 2; private readonly int _maxGram = 20; public PrefixSubwordAnalyzer(LuceneVersion luceneVersion) { _version = luceneVersion; } protected override TokenStreamComponents CreateComponents(string fieldName, TextReader reader) { Tokenizer tokenizer = new KeywordTokenizer(reader); // 从词的开头拆分指定长度的子词 TokenStream filter = new EdgeNGramTokenFilter(_version, tokenizer, _minGram, _maxGram); return new TokenStreamComponents(tokenizer, filter); } }
2. 任意位置子词匹配场景(例:搜mobile也能命中automobile)
适合全子串检索需求,使用NGramFilter实现,代码如下:
public class FullSubwordAnalyzer : Analyzer { private readonly LuceneVersion _version; // 子词最小/最大长度,可根据业务调整 private readonly int _minGram = 2; private readonly int _maxGram = 20; public FullSubwordAnalyzer(LuceneVersion luceneVersion) { _version = luceneVersion; } protected override TokenStreamComponents CreateComponents(string fieldName, TextReader reader) { Tokenizer tokenizer = new KeywordTokenizer(reader); // 拆分全位置的指定长度子词 TokenStream filter = new NGramTokenFilter(_version, tokenizer, _minGram, _maxGram); return new TokenStreamComponents(tokenizer, filter); } }
注意事项
- 请根据业务场景合理调整
_minGram和_maxGram的取值,_maxGram建议设置为字段允许的最长词长度即可,避免生成大量无效Token占用索引空间 - 检索阶段请使用
KeywordAnalyzer处理检索词,保证检索词作为完整Token和索引中的子词做匹配,不要使用和索引侧相同的子词分析器,否则会导致检索结果异常
内容的提问来源于stack exchange,提问作者Janski
相关产品推荐
相关产品推荐

