Elasticsearch 7.2.0多词自动补全问题:返回完整标题而非片段
解决Elasticsearch多词补全片段的问题
我明白你想要的是补全片段而非完整标题的需求,当前用completion suggester返回完整标题是因为它的设计逻辑就是返回你存入该字段的完整内容——默认情况下你是把整个电影名存在name.completion里,所以结果自然是完整标题。下面给你两种可行的解决方案:
方案一:手动/自动生成补全片段存入Completion字段
Completion suggester支持传入数组形式的候选补全项,你可以把电影名拆分成多个层级的补全片段,存入name.completion字段,这样查询时就能返回这些片段了。
步骤1:调整文档写入内容
比如对于《Avengers: Infinity War Part 2》,你可以把拆好的片段数组传给name.completion:
POST movies/_doc/3 { "name": "Avengers: Infinity War Part 2", "completion_terms": [ "avengers", "infinity", "war", "part", "2" ], "name": { "completion": [ "avengers", "avengers infinity", "avengers infinity war", "avengers infinity war part 2", "infinity", "infinity war", "infinity war part 2" ] } }
步骤2:保持原查询不变
继续用你之前的suggest查询,就能得到你想要的补全片段了:
POST movies/_search { "suggest": { "movie-suggest-fuzzy": { "prefix": "avan", "completion": { "field": "name.completion", "fuzzy": { "fuzziness": 1 } } } } }
优化:用Ingest Pipeline自动拆分片段
手动拆分太麻烦?可以用Elasticsearch的Ingest Pipeline自动生成这些片段。比如创建一个管道,用脚本把电影名拆分成不同长度的短语:
PUT _ingest/pipeline/movie_completion_pipeline { "processors": [ { "script": { "source": """ def parts = ctx.name.toLowerCase().replaceAll(/[:]/, '').split(' '); def completions = []; for (int i = 0; i < parts.length; i++) { def phrase = []; for (int j = i; j < parts.length; j++) { phrase.add(parts[j]); completions.add(phrase.join(' ')); } } ctx.name.completion = completions; """ } } ] }
写入文档时指定管道:
POST movies/_doc/3?pipeline=movie_completion_pipeline { "name": "Avengers: Infinity War Part 2", "completion_terms": [ "avengers", "infinity", "war", "part", "2" ] }
方案二:用Edge Ngram + Shingle分析器 + Terms聚合
如果你不想改动文档结构,可以通过自定义分析器生成短语片段,再用聚合提取结果。
步骤1:修改索引映射
更新你的索引分析器和映射,添加shingle分析器和对应的字段:
PUT movies/_mapping { "properties": { "name": { "type": "text", "fields": { "keywordstring": { "type": "text", "analyzer": "keyword_analyzer" }, "edgengram": { "type": "text", "analyzer": "edge_ngram_analyzer", "search_analyzer": "edge_ngram_search_analyzer" }, "completion": { "type": "completion" }, "shingles": { "type": "text", "analyzer": "shingle_analyzer", "fielddata": true } }, "analyzer": "standard" }, "completion_terms": { "type": "text", "fielddata": true, "analyzer": "completion_analyzer" } } } PUT movies/_settings { "analysis": { "filter": { "shingle_filter": { "type": "shingle", "min_shingle_size": 1, "max_shingle_size": 4, "output_unigrams": true } }, "analyzer": { "shingle_analyzer": { "tokenizer": "standard", "filter": [ "lowercase", "shingle_filter" ] } } } }
步骤2:重新索引文档
因为修改了分析器,需要重新索引现有文档才能生成shingles字段的内容。
步骤3:查询获取补全片段
用match_phrase_prefix查询配合terms聚合提取匹配的片段:
POST movies/_search { "size": 0, "query": { "match_phrase_prefix": { "name.shingles": { "query": "avengers inf" } } }, "aggs": { "suggestions": { "terms": { "field": "name.shingles", "size": 10, "order": { "_count": "desc" } } } } }
这两种方案都能满足你获取补全片段的需求,方案一更适合追求补全性能的场景(Completion suggester是基于前缀树的,速度更快),方案二更适合不想改动文档写入逻辑的场景。
内容的提问来源于stack exchange,提问作者Dobra Adrian
相关产品推荐
相关产品推荐

