ElasticSearch 8.10.2 当前/下一词自动补全实现咨询
基于Elasticsearch 8.10.2实现词级前缀补全方案
针对你的需求,结合Elasticsearch的特性,以下是两种可行的实现方案:
方案一:Completion Suggester(推荐,性能最优)
Completion Suggester是ES专为前缀补全设计的组件,结合数据预处理和结果后处理可以完全满足你的需求,且性能优异。
步骤1:数据预处理
将每个原始查询拆分为所有可能的"词序列前缀",比如:
- 原始查询
"iphone 10"→ 生成["iphone"]、["iphone", "10"]两个序列 - 原始查询
"iphone 10 case"→ 生成["iphone"]、["iphone", "10"]、["iphone", "10", "case"]三个序列 - 原始查询
"iphone 11"→ 生成["iphone"]、["iphone", "11"]两个序列 - 原始查询
"iphqqq"→ 生成["iphqqq"]一个序列
步骤2:创建索引映射
定义completion类型的字段,用于存储预处理后的词序列:
{ "mappings": { "properties": { "suggest": { "type": "completion", "preserve_separators": true, // 保留空格分隔符,确保词序列按空格拆分 "preserve_position_increments": true } } } }
步骤3:插入预处理后的文档
将每个生成的词序列作为单独文档插入索引:
// 插入["iphone"]序列 { "suggest": { "input": ["iphone"] } } // 插入["iphone", "10"]序列 { "suggest": { "input": ["iphone", "10"] } } // 插入["iphone", "10", "case"]序列 { "suggest": { "input": ["iphone", "10", "case"] } } // 插入["iphone", "11"]序列 { "suggest": { "input": ["iphone", "11"] } } // 插入["iphqqq"]序列 { "suggest": { "input": ["iphqqq"] } }
步骤4:构造查询并处理结果
根据用户输入的内容,构造对应的前缀查询,并开启去重:
- 示例1:输入
"iph"的查询DSL
{ "suggest": { "word_suggest": { "prefix": "iph", "completion": { "field": "suggest", "skip_duplicates": true, // 开启结果去重 "size": 10 } } } }
返回结果中,提取每个选项的text字段的最后一个词,得到["iphone", "iphqqq"]。
- 示例2:输入
"iphone"的查询DSL
{ "suggest": { "word_suggest": { "prefix": "iphone", "completion": { "field": "suggest", "skip_duplicates": true, "size": 10 } } } }
返回结果的text字段为"iphone 10"、"iphone 11",提取每个text的第二个词,得到["10", "11"]。
- 示例3:输入
"iphone 10"的查询DSL
{ "suggest": { "word_suggest": { "prefix": "iphone 10", "completion": { "field": "suggest", "skip_duplicates": true, "size": 10 } } } }
返回结果的text字段为"iphone 10 case",提取第三个词得到["case"]。
方案二:分词+前缀聚合(无需预处理,适合小数据量)
如果不想做复杂的数据预处理,可以将原始查询存入text字段,通过前缀查询结合聚合实现需求,但性能略低于Completion Suggester。
步骤1:创建索引映射
{ "mappings": { "properties": { "query_text": { "type": "text", "analyzer": "standard", // 按空格分词 "fielddata": true // 允许对text字段进行聚合 } } } }
步骤2:插入原始查询文档
直接插入原始查询内容:
{ "query_text": "iphone 10" } { "query_text": "iphone 10 case" } { "query_text": "iphone 11" } { "query_text": "iphqqq" }
步骤3:构造查询并处理结果
根据输入的词数和前缀,编写对应的查询和聚合:
- 输入
"iph":查询所有分词后包含以iph开头的词的文档,聚合去重这些词
{ "size": 0, "aggs": { "suggest_words": { "terms": { "script": { "source": "doc['query_text'].values.stream().filter(word -> word.startsWith(params.prefix)).collect(Collectors.toSet())", "params": { "prefix": "iph" } }, "size": 10 } } } }
聚合结果直接返回去重后的["iphone", "iphqqq"]。
- 输入
"iphone":查询包含iphone的文档,聚合提取iphone之后的词
{ "size": 0, "query": { "match": { "query_text": "iphone" } }, "aggs": { "next_words": { "terms": { "script": { "source": "def words = doc['query_text'].values; def idx = words.indexOf('iphone'); if (idx != -1 && idx < words.length - 1) { return words[idx+1]; }", "size": 10 } } } } }
聚合结果返回["10", "11"]。
关键说明
- Completion Suggester的
skip_duplicates参数(ES 7.6+支持,你的8.10.2版本兼容)可以直接实现结果去重,无需额外处理。 - 无论哪种方案,提取目标词(当前词或下一词)都需要简单的后处理逻辑,比如拆分返回的文本序列取对应位置的词。
内容的提问来源于stack exchange,提问作者Tim
相关产品推荐
相关产品推荐

