简介
现代的搜索引擎,一般都会提供 Suggest as you type 的功能,帮助用户在输入搜索的过程中,进行自动补全或者纠错。通过协助用户输入更加精准的关键词,提高后续搜索阶段文档匹配的程度。在 google 上搜索,一开始会自动补全。当输入到一定长度,如因为单词拼写错误无法补全,就会开始提示相似的词或者句子。
1. Suggester API
搜索引擎中类似的功能,在 ES 中通过 Sugester API 实现的
- 原理:将输入的文档分解为 Token,然后在索引的字段里查找相似的 Term 并返回
- 根据不同的使用场景,ES 设计了 4 种类别的 Suggesters
- Term
- Phrase Suggester
- Complete
- Context Suggester
Term Suggester
Suggester 就是一种特殊类型的搜索。“text” 里是调用时候提供的文本,通常来自用户界面上用户输入的内容。用户输入的 “lucen” 是一个错误的拼写会到 指定的字段 “body” 上搜索,当无法搜索到结果时(missing),返回建议的词。
PUT /suggest_article/
{
"mappings": {
"_doc": {
"properties": {
"body": {
"type": "text"
}
}
}
}
}
PUT suggest_article/_doc/1
{
"body":"lucene is very cool"
}
"body":"Elasticsearch builds on top of lucene"
"body":"Elasticsearch rocks"
"body":"elastic is the company behind ELK stack"
"body":"Elk stack rocks"
"body":"elasticsearch is rock solid"
搜索:
POST suggest_article/_search
{
"from": 0,
"size": 10,
"query": {
"match": {
"body": "lucen rock"
}
},
"suggest": {
"term-suggestion": {
"text": "lucen rock",
"term": {
"suggest_mode": "missing", // popular always
"field": "body"
}
}
}
}
结果:
{
"took" : 38,
"timed_out" : false,
"_shards" : {
"total" : 5,
"successful" : 5,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : 1,
"max_score" : 1.1149852,
"hits" : [
{
"_index" : "suggest_article",
"_type" : "_doc",
"_id" : "6",
"_score" : 1.1149852,
"_source" : {
"body" : "elasticsearch is rock solid"
}
}
]
},
"suggest" : {
"term-suggestion" : [
{
"text" : "lucen",
"offset" : 0,
"length" : 5,
"options" : [
{
"text" : "lucene",
"score" : 0.8,
"freq" : 2
}
]
},
{
"text" : "rock",
"offset" : 6,
"length" : 4,
"options" : [
{
"text" : "rocks",
"score" : 0.75,
"freq" : 2
}
]
}
]
}
}
词条推荐器使用了Lucene的错拼检查器模块,该模块会根据给定词条的编辑距离(es使用了叫做Levenstein edit distance的算法,其核心思想就是一个词改动多少字符就可以和另外一个词一致),从索引中返回最大编辑距离不超过某个值的那些词条。比如说为了从mik
得到mick
,需要加入一个字母(也就是说需要至少要改动一次),所以这两个词的编辑距离就是1。
text:建议文本,建议文本是必需的选项,可以通过全局(多个建议器中查询相同的内容)或者按照单个建议器的格式来。
field:从field字段中获取候选建议的字段。这是一个必需的选项,需要全局设置或根据建议设置。
analyzer:用于分析建议文本的分析器。默认为建议字段的搜索分析器。
size:个建议文本标记返回的最大条目。
sort:定义如何根据建议文本术语对建议进行排序。它有两个可能的值。
score,先按分数排序,然后按文档频率排序,再按术语本身排序。
frequency,首先按文档频率排序,然后按相似性分数排序,然后按术语本身排序。也可以理解为按照流行度排序。
suggest_mode:控制建议的模式,有3个模式可选择。
missing,仅为不在索引中的建议文本术语提供建议。这是默认值。
popular,仅建议在比原始建议文本术语更多的文档中出现的建议。也就是说提供比原有输入词频更高的词条
always,根据建议文本中的条款建议任何匹配的建议。说白了就是无论如何都会提供建议。
lowercase_terms:在文本分析之后降低建议文本术语的大小写。
min_word_length:建议文本术语必须具有的最小长度才能包含在内。默认为4.(旧名称min_word_len已弃用)。
shard_size:设置从每个单独分片中检索的最大建议数。
小结:
term suggester
首先将输入文本经过分析器(所以,分析结果由于采用的分析器不同而有所不同)分析,处理为单个词条,然后根据单个词条去提供建议,并不会考虑多个词条之间的关系。然后将每个词条的建议结果(有或没有)封装到options
列表中。最后由推荐器统一返回。
Phrase Suggester
Phrase suggester在Term suggester的基础上,会考量多个term之间的关系,比如是否同时出现在索引的原文里,相邻程度,以及词频等等。
- Suggeset Mode : missing,popular ,always
- Max Errors: 最多可以拼错的 Terms 数
- Condfidence : 限制返回结果数,默认为 1
POST suggest_article/_search
{
"suggest": {
"my-suggestion": {
"text": "lucne and elasticsear rock",
"phrase": {
"field": "body",
"max_errors":2, # 最多可以拼错的terms
"confidence":0, # 限制返回的结果数,默认是1
"direct_generator":[{
"field":"body",
"suggest_mode":"always"
}],
"highlight": {
"pre_tag": "<em>",
"post_tag": "</em>"
}
}
}
}
}
{
"took" : 99,
"timed_out" : false,
"_shards" : {
"total" : 5,
"successful" : 5,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : 0,
"max_score" : 0.0,
"hits" : [ ]
},
"suggest" : {
"my-suggestion" : [
{
"text" : "lucne and elasticsear rock",
"offset" : 0,
"length" : 26,
"options" : [
{
"text" : "lucne and elasticsearch rocks",
"highlighted" : "lucne and <em>elasticsearch rocks</em>",
"score" : 0.12709484
},
{
"text" : "lucne and elasticsearch rock",
"highlighted" : "lucne and <em>elasticsearch</em> rock",
"score" : 0.10422645
},
{
"text" : "lucne and elasticsear rocks",
"highlighted" : "lucne and elasticsear <em>rocks</em>",
"score" : 0.10036137
},
{
"text" : "lucne and elasticsear rock",
"highlighted" : "lucne and elasticsear rock",
"score" : 0.082303174
},
{
"text" : "lucene and elasticsear rock",
"highlighted" : "<em>lucene</em> and elasticsear rock",
"score" : 0.030959692
}
]
}
]
}
}
自定义高亮:
"pre_tag":"<b id='d1' class='t1' style='color:red;font-size:18px;'>",
"post_tag":"</b>"
注意:推荐器结果的高亮显示和查询结果高亮显示有些许区别,比如说,这里的自定义标签是pre_tag和post_tag而不是之前的pre_tags和post_tags
Completion Suggester
主要针对的应用场景就是"Auto Completion"。 此场景下用户每输入一个字符的时候,就需要即时发送一次查询请求到后端查找匹配项,在用户输入速度较高的情况下对后端响应速度要求比较苛刻。因此实现上它和前面两个Suggester采用了不同的数据结构,索引并非通过倒排来完成,而是将analyze过的数据编码成FST和索引一起存放。对于一个open状态的索引,FST会被ES整个装载到内存里的,进行前缀查找速度极快。但是FST只能用于前缀查找,这也是Completion Suggester的局限所在。
PUT /completion_article/
{
"mappings": {
"_doc": {
"properties": {
"body": {
"type": "completion"
}
}
}
}
}
PUT completion_article/_doc/1
{
"body":"lucene is very cool"
}
"body":"Elasticsearch builds on top of lucene"
"body":"Elasticsearch rocks"
"body":"elastic is the company behind ELK stack"
"body":"Elk stack rocks"
"body":"elasticsearch is rock solid"
POST completion_article/_search
{ "size": 0,
"suggest": {
"completion-suggest": {
"prefix": "elastic i",
"completion": {
"field": "body"
}
}
}
}
{
"took" : 42,
"timed_out" : false,
"_shards" : {
"total" : 5,
"successful" : 5,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : 0,
"max_score" : 0.0,
"hits" : [ ]
},
"suggest" : {
"completion-suggest" : [
{
"text" : "elastic i",
"offset" : 0,
"length" : 9,
"options" : [
{
"text" : "elastic is the company behind ELK stack",
"_index" : "completion_article",
"_type" : "_doc",
"_id" : "4",
"_score" : 1.0,
"_source" : {
"body" : "elastic is the company behind ELK stack"
}
}
]
}
]
}
}
值得注意的一点是Completion Suggester在索引原始数据的时候也要经过analyze阶段,选用的analyzer不同,某些词可能会被转换或者某些词可能被去除,这些会影响FST编码结果,也会影响查找匹配的效果。
比如我们重新索引,设置索引的mapping,将analyzer更改为"english"
来源:oschina
链接:https://my.oschina.net/u/3727895/blog/4790988