0%

ElasticSearch-搜索建议 Suggester

相关阅读: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 则由于设置了 missingsuggest_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 已经存在,所以不会在搜索建议中返回。

指定 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 遵循如下步骤:

  1. 定义 Mapping,使⽤ “completion” type
  2. 索引数据
  3. 运⾏ “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 的具体步骤:

  1. 定制⼀个 Mapping
  2. 索引数据,并且为每个⽂档加⼊ Context 信息
  3. 结合 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"
}
}
}
}
}

响应结果:

------ 本文结束------