相关阅读:https://www.elastic.co/guide/en/elasticsearch/reference/7.4/search-suggesters.html
1. 搜索建议 现代的搜索引擎,⼀般都会提供 Suggest as you type 的功能。以帮助⽤户在输⼊搜索的过程中,进⾏⾃动补全或者纠错。通过协助⽤户输⼊更加精准的关键词,提⾼后续搜索阶段⽂档匹配的程度。
在 google 上搜索,⼀开始会⾃动补全。当输⼊到⼀定⻓度,如因为单词拼写错误⽆法补全, 就会开始提示相似的词或者句⼦:
2. Suggester API 搜索引擎中类似的功能,在 Elasticsearch 中是通过 Suggester API 实现的。其原理是将输⼊的⽂本分解为 Token,然后在索引的字典⾥查找相似的 Term 并返回。根据不同的使⽤场景,Elasticsearch 设计了 4 种类别的 Suggesters:
Term & Phrase Suggester Complete & Context Suggester 3. Term Suggester 为了方便演示,我们先增加对应索引和文档,具体如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 POST articles/_bulk { "index" : { } } { "body" : "lucene is very cool" } { "index" : { } } { "body" : "Elasticsearch builds on top of lucene" } { "index" : { } } { "body" : "Elasticsearch rocks" } { "index" : { } } { "body" : "elastic is the company behind ELK stack" } { "index" : { } } { "body" : "Elk stack rocks" } { "index" : {} } { "body" : "elasticsearch is rock solid" } { "index" : {} }
Suggester 就是⼀种特殊类型的搜索。观察如下查询示例,text
字段⾥是调⽤时候提供的⽂本,通常来⾃于⽤户界⾯上⽤户输⼊的内容。注意这里⽤户输⼊的 “lucen” 是⼀个错误的拼写,实际用户要输入的是“lucene”。field
指明会到指定的字段 body
上搜索,当⽆法搜索到结果时 (missing),不会返回建议的词。
1 2 3 4 5 6 7 8 9 10 11 12 POST /articles/_search { "suggest" : { "term-suggestion" : { "text" : "lucen rock" , "term" : { "suggest_mode" : "missing" , "field" : "body" } } } }
上述查询响应结果如下,可以发现对于 lucen
会返回搜索建议lucene
,而rock
则由于设置了 missing
的suggest_mode
,因为不会返回搜索推荐。
当然我们一般是再进行搜索时附带上搜索建议的,查询 DSL 和响应如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 POST /articles/_search { "query" : { "match" : { "body" : "lucen rock" } }, "suggest" : { "term-suggestion" : { "text" : "lucen rock" , "term" : { "suggest_mode" : "missing" , "field" : "body" } } } }
3.1 Missing Mode 当搜索 “lucen rock”时,每个建议都包含了⼀个算分,相似性是通过 Levenshtein Edit Distance 的算法实现的。核⼼思想就是⼀个词改动多少字符就可以和另外⼀个词⼀致。 提供了很多可选参数来控制相似性的模糊程度。例如 “max_edits”。Term Suggester 提供了几种 suggest_mode,其中 missing 表示如果索引中已经存在,就不提供建议。例如上述示例中分词 rock
已经存在,所以不会在搜索建议中返回。
3.2 Popular Mode 指定 Popular Mode,会推荐出现频率更加⾼的词,例如下述查询语句中,虽然索引中既存在 rock
,也存在 rocks
。但是rocks
出现的频率更高,所有搜索推荐会返回 rocks
。
1 2 3 4 5 6 7 8 9 10 11 12 13 POST /articles/_search { "suggest" : { "term-suggestion": { // term-suggestion 为自定义名称 "text": "lucen rock", "term": { "suggest_mode": "popular", "field": "body" } } } }
响应结果如下:
3.3 Always Mode suggest_mode 指定 always 时,索引中的分词⽆论是否存在,都提供建议。
1 2 3 4 5 6 7 8 9 10 11 12 13 POST /articles/_search { "suggest" : { "term-suggestion" : { "text" : "lucen rock" , "term" : { "suggest_mode" : "always" , "field" : "body" } } } }
响应结果如下:
3.4 sort 搜索建议默认按照 score 排序,也可以按照 frequency
,即词项出现的频率。示例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 POST /articles/_search { "suggest" : { "term-suggestion" : { "text" : "lucen cock" , "term" : { "suggest_mode" : "always" , "field" : "body" , "sort" : "frequency" } } } }
响应结果:
3.5 Prefix Length 默认⾸字⺟不⼀致就不会匹配推荐,但是如果将 prefix_length 设置为 0,就会为 hock 建议 rock:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 POST /articles/_search { "suggest" : { "term-suggestion" : { "text" : "lucen hocks" , "term" : { "suggest_mode" : "always" , "field" : "body" , "prefix_length" :0 , "sort" : "frequency" } } } }
4. Phrase Suggester Phrase Suggester 在 Term Suggester 上增加了⼀些额外的逻辑,其提供更丰富的API,比如通过指定 highlight
对搜索建议的词进行高亮显示,max_errors
设置最多可以拼错的 Terms 数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 POST /articles/_search { "suggest" : { "my-suggestion" : { "text" : "lucne and elasticsear rock hello world " , "phrase" : { "field" : "body" , "max_errors" :2 , "direct_generator" :[{ "field" :"body" , "suggest_mode" :"always" }], "highlight" : { "pre_tag" : "<em>" , "post_tag" : "</em>" } } } } }
查询响应:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 { // ... "suggest" : { "my-suggestion" : [ { "text" : "lucne and elasticsear rock hello world " , "offset" : 0 , "length" : 39 , "options" : [ { "text" : "lucene and elasticsearch rock hello world" , "highlighted" : "<em>lucene</em> and <em>elasticsearch</em> rock hello world" , "score" : 1.5788074E-4 }, { "text" : "lucne and elasticsearch rocks hello world" , "highlighted" : "lucne and <em>elasticsearch rocks</em> hello world" , "score" : 1.136111E-4 }, { "text" : "lucne and elasticsearch rock hello world" , "highlighted" : "lucne and <em>elasticsearch</em> rock hello world" , "score" : 1.05567684E-4 }, { "text" : "lucene and elasticsear rocks hello world" , "highlighted" : "<em>lucene</em> and elasticsear <em>rocks</em> hello world" , "score" : 9.929376E-5 }, { "text" : "lucene and elasticsear rock hello world" , "highlighted" : "<em>lucene</em> and elasticsear rock hello world" , "score" : 9.2263974E-5 } ] } ] } }
5. Completion Suggester Completion Suggester 提供了“⾃动完成” (Auto Complete) 的功能。⽤户每输⼊⼀个字符,就需要即时发送⼀个查询请求到后端查找匹配项。其对性能要求⽐较苛刻。Elasticsearch 采⽤了不同的数据结构,并⾮通过倒排索引来完成。 ⽽是将 Analyze 的数据编码成 FST 和索引⼀起存放。FST 会被 ES 整个加载进内存, 速度很快。但是 FST 只能⽤于前缀查找。
使⽤ Completion Suggester 遵循如下步骤:
定义 Mapping,使⽤ “completion” type 索引数据 运⾏ “suggest” 查询,得到搜索建议 首先定义如下 mapping:
1 2 3 4 5 6 7 8 9 10 PUT articles { "mappings" : { "properties" : { "title_completion" :{ "type" : "completion" } } } }
然后增加如下索引和文档:
1 2 3 4 5 6 7 8 9 10 11 12 POST articles/_bulk { "index" : { } } { "title_completion" : "lucene is very cool" } { "index" : { } } { "title_completion" : "Elasticsearch builds on top of lucene" } { "index" : { } } { "title_completion" : "Elasticsearch rocks" } { "index" : { } } { "title_completion" : "elastic is the company behind ELK stack" } { "index" : { } } { "title_completion" : "Elk stack rocks" } { "index" : {} }
我们即可用下述 DSL 进行查询,其中 prefix
指定查询前缀文本:
1 2 3 4 5 6 7 8 9 10 11 POST articles/_search?pretty // pretty作用是格式化查询结果 { "suggest" : { "article-suggester" : { "prefix" : "ela" , "completion" : { "field" : "title_completion" } } } }
查询响应结果如下,其中 title 字段中前缀含有 ela
会被返回:
6. Context Suggester Context Suggester 是 Completion Suggester 的扩展,可以在搜索中加⼊更多的上下⽂信息,例如,输⼊ “star”,如果指明与咖啡相关,则会建议 “Starbucks”,若与电影相关,则建议 ”star wars”。
Context Suggester 可以定义两种类型的 Context:
Category – 任意的字符串 Geo – 地理位置信息 实现 Context Suggester 的具体步骤:
定制⼀个 Mapping 索引数据,并且为每个⽂档加⼊ Context 信息 结合 Context 进⾏ Suggestion 查询 首先定义一个索引和 mapping,增加 contexts 并指明其 type 和 name:
1 2 3 4 5 6 7 8 9 10 11 12 13 PUT comments PUT comments/_mapping { "properties" : { "comment_autocomplete" :{ "type" : "completion" , "contexts" :[{ "type" :"category" , "name" :"comment_category" }] } } }
接着添加两个文档,并为数据设置对应 comment_category:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 POST comments/_doc { "comment" :"I love the star war movies" , "comment_autocomplete" :{ "input" :["star wars" ], "contexts" :{ "comment_category" :"movies" } } } POST comments/_doc { "comment" :"Where can I find a Starbucks" , "comment_autocomplete" :{ "input" :["starbucks" ], "contexts" :{ "comment_category" :"coffee" } } }
当我们使用 context suggest 进行检索时,只需要指明 comment_category 的值,就会返回对应类别的建议文本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 POST comments/_search { "suggest" : { "MY_SUGGESTION" : { "prefix" : "sta" , "completion" :{ "field" :"comment_autocomplete" , "contexts" :{ "comment_category" :"coffee" } } } } }
响应结果: