如何在Solr、ElasticSearch中实现文档值到搜索词的前缀匹配
实现文档值到搜索词的前缀匹配(Solr/ElasticSearch)
问题背景
Solr、ElasticSearch 默认支持 <search term> -> <document value> 的前缀匹配逻辑:比如用搜索词 "travel*",可以匹配字段值为 "traveling the world" 的文档(因为文档值以搜索词为前缀)。
但我们需要的是反向前缀匹配:<document value> -> <search term>,即当搜索词是 "traveling the world" 时,要能命中字段值为 "travel" 的文档(因为文档值是搜索词中"traveling"的前缀)。
解决方案
1. Edge Ngram 分词方案(推荐,性能优)
通过在索引阶段生成文档字段值的所有前缀词项,让搜索词的分词结果能匹配到这些前缀。
ElasticSearch 配置示例
- 定义字段的自定义分析器:
{ "settings": { "analysis": { "analyzer": { "prefix_analyzer": { "tokenizer": "keyword", "filter": ["lowercase", "edge_ngram"] } }, "filter": { "edge_ngram": { "type": "edge_ngram", "min_gram": 2, "max_gram": 20 } } } }, "mappings": { "properties": { "target_field": { "type": "text", "analyzer": "prefix_analyzer", "search_analyzer": "standard" } } } } - 索引字段值
"travel"时,会生成"tr", "tra", "trave", "travel"等前缀词项;查询"traveling the world"时,分词后的"traveling"会匹配到"travel"对应的前缀词项,从而命中文档。
Solr 配置示例
- 在
schema.xml中定义 EdgeNGram 字段类型:<fieldType name="text_edge_ngram" class="solr.TextField"> <analyzer type="index"> <tokenizer class="solr.KeywordTokenizerFactory"/> <filter class="solr.LowerCaseFilterFactory"/> <filter class="solr.EdgeNGramFilterFactory" minGramSize="2" maxGramSize="20"/> </analyzer> <analyzer type="query"> <tokenizer class="solr.StandardTokenizerFactory"/> <filter class="solr.LowerCaseFilterFactory"/> </analyzer> </fieldType> - 定义字段
<field name="target_field" type="text_edge_ngram" stored="true" indexed="true"/>,后续查询逻辑同 ElasticSearch。
2. 反转字段方案
通过反转文档字段值和搜索词,将反向前缀匹配转化为常规前缀匹配。
ElasticSearch 实现
- 定义反转字段的分析器:
{ "settings": { "analysis": { "analyzer": { "reverse_analyzer": { "tokenizer": "keyword", "filter": ["lowercase", "reverse"] } } } }, "mappings": { "properties": { "target_field": {"type": "text"}, "target_field_rev": { "type": "text", "analyzer": "reverse_analyzer", "search_analyzer": "reverse_analyzer" } } } } - 索引时同时存储原始字段和反转字段(比如
"travel"反转后为"levart");查询时,先将搜索词"traveling the world"处理为反转后的"dlrow eht gnilevart",然后用前缀查询:{ "query": { "prefix": { "target_field_rev": "levart" } } }
Solr 实现
- 在
schema.xml中定义带反转过滤器的字段类型:<fieldType name="text_rev" class="solr.TextField"> <analyzer type="index"> <tokenizer class="solr.KeywordTokenizerFactory"/> <filter class="solr.LowerCaseFilterFactory"/> <filter class="solr.ReversedWildcardFilterFactory" withOriginal="true"/> </analyzer> <analyzer type="query"> <tokenizer class="solr.KeywordTokenizerFactory"/> <filter class="solr.LowerCaseFilterFactory"/> </analyzer> </fieldType> - 定义反转字段
<field name="target_field_rev" type="text_rev" stored="false" indexed="true"/>,查询时直接使用target_field_rev:traveling*即可匹配到原始值为"travel"的文档。
3. 脚本/通配符查询(不推荐,性能差)
适合小数据集临时测试,大数据量下会严重影响查询性能。
ElasticSearch 脚本查询示例
{ "query": { "script": { "script": { "source": "params.searchTerm.startsWith(doc['target_field'].value)", "params": { "searchTerm": "traveling the world" } } } } }
Solr 通配符查询示例
构造查询条件,让文档字段值作为前缀匹配搜索词:
q=target_field:*&fq={!frange l=0 u=999}strlen(target_field) AND target_field:* AND "{!func}contains('traveling the world', target_field)"
内容的提问来源于stack exchange,提问作者RagingBull
相关产品推荐
相关产品推荐

