原文链接:https://www.jianshu.com/p/7f41c6725706

Elasticsearch里设计了4种类别的Suggester,分别是:

  • Term Suggester : 术语推荐
  • Phrase Suggester : 短语推荐
  • Completion Suggester : 完成推荐 也称:auto completion
  • Context Suggester: 上下文推荐

初始化环境

**创建索引**
这里创建了一个新闻的索引。里面有两个字段title和body,title使用的是ik_smart分析器,body使用的是ik_max_word分析器

  1. PUT /news
  2. {
  3. "mappings": {
  4. "properties": {
  5. "title": {
  6. "type": "text",
  7. "analyzer": "ik_smart",
  8. "fields": {
  9. "kw": {
  10. "type": "completion",
  11. "analyzer": "keyword"
  12. },
  13. "py": {
  14. "type": "completion",
  15. "analyzer": "pinyin"
  16. }
  17. }
  18. },
  19. "body": {
  20. "type": "text",
  21. "analyzer": "ik_max_word",
  22. "fields": {
  23. "kw": {
  24. "type": "keyword",
  25. "analyzer": "completion"
  26. }
  27. }
  28. }
  29. }
  30. }
  31. }

**插入数据**

  1. POST _bulk
  2. {"index":{"_index":"news","_id":"1"}}
  3. {"title":"乌鲁木齐惊现彩虹","body":"今日午后一场大雨过后,乌鲁木齐天空上出现一道彩虹"}
  4. {"index":{"_index":"news","_id":"2"}}
  5. {"title":"乌鲁木齐发展很好","body":"乌鲁木齐发展很好,很多美丽的景点,是个旅游的好去处"}
  6. {"index":{"_index":"news","_id":"3"}}
  7. {"title":"乌鲁木齐的景点","body":"乌鲁木齐的景点分布以乌鲁木齐为中心,东、南、西、北四个方向"}
  8. {"index":{"_index":"news","_id":"4"}}
  9. {"title":"南昌最好的大学江西师范大学","body":"江西师范大学是南昌一个知名大高校"}
  10. {"index":{"_index":"news","_id":"5"}}
  11. {"title":"江西师范大学","body":"江西师范大学简称“江西师大”,位于历史文化名城江西省会南昌市,是教育部、江西省人民政府共建高校"}
  12. {"index":{"_index":"news","_id":"6"}}
  13. {"title":"鄱阳湖","body":"鄱阳湖,古称彭蠡、彭蠡泽、彭泽,位于江西省北部,地处九江、南昌、上饶三市,是中国第一大淡水湖,也是中国第二大湖,仅次于青海湖"}

测试分析器效果:

  1. GET news/_analyze
  2. {
  3. "field": "title",
  4. "text": "乌鲁木齐惊现彩虹"
  5. }

结果如下:

  1. {
  2. "tokens" : [
  3. {
  4. "token" : "乌鲁木齐",
  5. "start_offset" : 0,
  6. "end_offset" : 4,
  7. "type" : "CN_WORD",
  8. "position" : 0
  9. },
  10. {
  11. "token" : "惊",
  12. "start_offset" : 4,
  13. "end_offset" : 5,
  14. "type" : "CN_CHAR",
  15. "position" : 1
  16. },
  17. {
  18. "token" : "现",
  19. "start_offset" : 5,
  20. "end_offset" : 6,
  21. "type" : "CN_CHAR",
  22. "position" : 2
  23. },
  24. {
  25. "token" : "彩虹",
  26. "start_offset" : 6,
  27. "end_offset" : 8,
  28. "type" : "CN_WORD",
  29. "position" : 3
  30. }
  31. ]
  32. }

可以看到使用ik_smart分析器时,乌鲁木齐是一个长度为4的术语,这也是我为什么使用这个地名的原因。

Term suggester

Term suggester提示器会根据所提供的文本建议外观相似的术语,建议功能的部分功能仍在开发中

  1. GET news/_search
  2. {
  3. "suggest": {
  4. "YOUR_SUGGESTION": {
  5. "text": "乌鲁木",
  6. "term": {
  7. "field": "title"
  8. }
  9. }
  10. }
  11. }

公共字段表:

字段 字段含义
text 建议文本是必需的选项,需要全局设置或针对每个建议设置(使用如下图)
field 要从中获取候选建议的字段。这是必需的选项,需要全局设置或针对每个建议设置
analyer 分析建议文本用的分析器。默认为建议字段的搜索分析器
size 每个建议文本标记返回的最大更正数量
sore 定义建议应该如何按建议文本项排序。两个可能的值有score和frequency
suggest_mode 建议模式控制控制文本术语在什么情况下应该被建议。可以指定三个可能的值
1. missing: 仅对索引中没有的建议文本术语提供建议。这是默认值
2. popular: 只建议出现在比原始建议文本术语更多的文档中的建议
2. always: 根据建议文本中的术语建议任何匹配的建议

结果如下:

  1. {
  2. "took" : 614,
  3. "timed_out" : false,
  4. "_shards" : {
  5. "total" : 1,
  6. "successful" : 1,
  7. "skipped" : 0,
  8. "failed" : 0
  9. },
  10. "hits" : {
  11. "total" : {
  12. "value" : 0,
  13. "relation" : "eq"
  14. },
  15. "max_score" : null,
  16. "hits" : [ ]
  17. },
  18. "suggest" : {
  19. "YOUR_SUGGESTION" : [
  20. {
  21. "text" : "乌",
  22. "offset" : 0,
  23. "length" : 1,
  24. "options" : [ ]
  25. },
  26. {
  27. "text" : "鲁",
  28. "offset" : 1,
  29. "length" : 1,
  30. "options" : [ ]
  31. },
  32. {
  33. "text" : "木",
  34. "offset" : 2,
  35. "length" : 1,
  36. "options" : [ ]
  37. }
  38. ]
  39. }
  40. }

这个返回的结果我们只需要关注suggest字段就行了,这里把我们的乌鲁木分成了三个字。分成三个字是因为字段title使用的是ik_smart ;为了将这三个字不分开,我们必须指定suggest中text使用的分词器,而不默认使用ik_smart。这时我就可以使用analyzer字段了。

  1. GET news/_search
  2. {
  3. "suggest": {
  4. "YOUR_SUGGESTION": {
  5. "text": "乌鲁木",
  6. "term": {
  7. "analyzer": "whitespace",
  8. "field": "title"
  9. }
  10. }
  11. }
  12. }

结果:

  1. "suggest" : {
  2. "YOUR_SUGGESTION" : [
  3. {
  4. "text" : "乌鲁木",
  5. "offset" : 0,
  6. "length" : 3,
  7. "options" : [ ]
  8. }
  9. ]
  10. }

但是你可以看到,options中还是没有任何提示。这是什么情况呢?这里就有一个比较大的一个问题了。中文和英文的差异问题,术语提示默认长度;
这里默认设置了术语必须长度达到4个字符才会进行术语匹配。在英文中四个字符就是四个字母,要达到要求很简单的,但是中文四个字符就是四个字,四个字才能触发提示。
那我们测试一个将术语改为四个字,如下:

  1. "suggest" : {
  2. "YOUR_SUGGESTION" : [
  3. {
  4. "text" : "乌鲁木大",
  5. "offset" : 0,
  6. "length" : 4,
  7. "options" : [
  8. {
  9. "text" : "乌鲁木齐",
  10. "score" : 0.75,
  11. "freq" : 3
  12. }
  13. ]
  14. }
  15. ]
  16. }

提示出现了,不过四个字才会提示有点坑爹。那我们把那个参数改成2个字试试。

  1. GET news/_search
  2. {
  3. "suggest": {
  4. "YOUR_SUGGESTION": {
  5. "text": "乌鲁木",
  6. "term": {
  7. "analyzer": "whitespace",
  8. "min_word_length": 2,
  9. "field": "title"
  10. }
  11. }
  12. }
  13. }

响应结果:

  1. "suggest" : {
  2. "YOUR_SUGGESTION" : [
  3. {
  4. "text" : "乌鲁木",
  5. "offset" : 0,
  6. "length" : 3,
  7. "options" : [
  8. {
  9. "text" : "乌鲁木齐",
  10. "score" : 0.6666666,
  11. "freq" : 3
  12. }
  13. ]
  14. }
  15. ]
  16. }

术语建议依然会有提示,这里有一个问题,乌鲁木齐四个字我们发送的乌鲁木对了三个字,但是score字段只有0.666,本来应该是0.75才是,上面测试的乌鲁木大的score值都是0.75。这说明缺少字,会影响score分值。

如果我们只用乌鲁两个字进行测试,那么score将会小于0.5,这就导致了无法出现提示了。但是如果我们使用乌鲁xx(x可以是不是空格字符的任意字符),那将会有建议出现

  1. GET news/_search
  2. {
  3. "suggest": {
  4. "two_words": {
  5. "text": "乌鲁 乌鲁00 乌鲁000",
  6. "term": {
  7. "analyzer": "whitespace",
  8. "min_word_length": 2,
  9. "field": "title"
  10. }
  11. }
  12. }
  13. }

结果:

  1. "suggest" : {
  2. "two_words" : [
  3. {
  4. "text" : "乌鲁",
  5. "offset" : 0,
  6. "length" : 2,
  7. "options" : [ ]
  8. },
  9. {
  10. "text" : "乌鲁00",
  11. "offset" : 3,
  12. "length" : 4,
  13. "options" : [
  14. {
  15. "text" : "乌鲁木齐",
  16. "score" : 0.5,
  17. "freq" : 3
  18. }
  19. ]
  20. },
  21. {
  22. "text" : "乌鲁000",
  23. "offset" : 8,
  24. "length" : 5,
  25. "options" : [ ]
  26. }
  27. ]
  28. }

测试说明字符少了或是多了都会影响分值的计算,分值少于0.5时,不会出现建议术语。
看到这里或许不用我说大家也知道了,这个术语建议在中文中并不好用,只能用于一些用户分好词以后的建议。比如这里我使用的分析器analyzer是whitespace,这就要求用户输入每一个术语时自己空格。这是不太友好的,如果使用ik_smart,他只会在乌鲁木齐完整词才会认为它是一个整体,但是一旦少了一个字,其他三个字就会被分开,这也是不行的。
对于英文我们可以直接使用standard分析器。但是中文不行。
下面我就把Term Suggester的其他可用的参数进行列举一下:

字段名称 作用
max_edits 表示被选为建议的edit distance的最大值,只能是1,2之间的数(可以是小数),默认是2,其他值会报错
prefix_length 表示被选为建议的最小前缀字符的长度,默认为1,增加这个长度可以提高拼写检查的性能,通常拼写错误不会发生在术语的最前面(prefix_len参数已经丢弃)
min_word_length 表示推荐文本的最小长度,默认为4(min_word_len参数已被丢弃)
shard_size 设置从每个碎片检索的建议的最大数量。在reduce阶段,根据size选项只返回前N个建议。默认是和size选项一样(如果不设置shard_size)。将此值设置为高于size的值可能有助于获得更准确的文档拼写更正频率,但会以性能为代价。由于术语是在切分之间分区的,因此切分级别的文档拼写更正频率可能并不精确。增加这个值将使这些文档频率更加精确。
max_inspections 表示一个因子,这个参数和shard_size参数相乘以便在碎片级别检查更多的候选者的拼写错误,可以提高以性能为代价的准确性,参数默认为5
min_doc_freq 表示一个建议中应包含文档数目的最小限制,可以指定为一个确切的数或文档数的相对百分比,可以通过推荐高频术语来提高质量,min_doc_freq默认是0(即不开启此功能),如果将这个参数指定为一个大于1的值(不能是小数,当大于1时不能指定为小数),碎片级别的文档频率用于这个选项
max_term_freq 表示推荐文本可以包含的文档数目的最大限制,可以是一个代表文档频率的确切值,也可以是一个相对百分数(比如0.4),如果指定为一个大于1的数(不可以指定为小数),默认是0.01f,这个参数可以用来排除高频术语的拼写检查,高频术语通常在前几个字符是拼写正确的以提高拼写检查的性能,碎片(分片)性能文档频率用于这个选项
string_distance 表示一个字符串距离用于和推荐内容相比它们之间的相似性,这个参数可能的值有5个:
internal:表示默认的基于damerau_levenshtein算法,但在比较字符串距离内的索引已经做过高度优化
damerau_levenshtein:是一种基于Damerau-Levenshtein算法的字符串距离算法
levenshtein:是一种基于Levenshtein edit distance算法的字符串距离算法
jaro_winkler:是一种基于Jaro-Winkler算法的字符串距离算法
ngram:是一种基于字符连词的字符串距离算法

感觉和最小编辑距离算法很类似

phrase suggester

phrase suggesterterm suggester相比,对建议的文本会参考上下文,也就是一个句子的其他token,不只是单纯的token距离匹配,它可以基于共生和频率选出更好的建议。

  1. GET news/_search
  2. {
  3. "suggest": {
  4. "YOUR_SUGGESTION": {
  5. "text": "乌鲁木q 先 彩虹",
  6. "phrase": {
  7. "analyzer": "whitespace",
  8. "field": "title"
  9. }
  10. }
  11. }
  12. }

结果

  1. "suggest" : {
  2. "YOUR_SUGGESTION" : [
  3. {
  4. "text" : "乌鲁木q 先 彩虹",
  5. "offset" : 0,
  6. "length" : 9,
  7. "options" : [
  8. {
  9. "text" : "乌鲁木齐 先 彩虹",
  10. "score" : 0.02188523
  11. }
  12. ]
  13. }
  14. ]
  15. }

可能有人会有疑问,为什么,这里中文也使用**analyzer:whitespace**?其实还是和之前说过的情况一样。因为索引中title字段使用的分析器是**ik_smart**,所以如果这里不指定默认就是和title字段一样的分析器,但是**ik_smart**分析器无法判断乌鲁木q是一个术语,他会将其拆分成乌,鲁,木,q,从而导致建议器无法提供任何建议。
请求: suggest_mode: always

  1. GET news/_search
  2. {
  3. "suggest": {
  4. "text": "乌1木q 现 虹",
  5. "simple_phrase": {
  6. "phrase": {
  7. "field": "title",
  8. "analyzer": "whitespace",
  9. "gram_size": 3,
  10. "direct_generator": [
  11. {
  12. "field": "title",
  13. "suggest_mode": "always"
  14. }
  15. ],
  16. "highlight": {
  17. "pre_tag": "<em>",
  18. "post_tag": "</em>"
  19. }
  20. }
  21. }
  22. }
  23. }

结果:

  1. "suggest" : {
  2. "simple_phrase" : [
  3. {
  4. "text" : "乌1木q 现 虹",
  5. "offset" : 0,
  6. "length" : 8,
  7. "options" : [
  8. {
  9. "text" : "乌鲁木齐 现 虹",
  10. "highlighted" : "<em>乌鲁木齐</em> 现 虹",
  11. "score" : 0.008279981
  12. }
  13. ]
  14. }
  15. ]
  16. }

Api 例子:

  1. GET news/_search
  2. {
  3. "suggest": {
  4. "text": "乌1木q 现 虹",
  5. "simple_phrase": {
  6. "phrase": {
  7. "field": "title",
  8. "analyzer": "whitespace",
  9. "gram_size": 3,
  10. "direct_generator": [
  11. {
  12. "field": "title",
  13. "suggest_mode": "always"
  14. }
  15. ],
  16. "highlight": {
  17. "pre_tag": "<em>",
  18. "post_tag": "</em>"
  19. },
  20. "collate": {
  21. "query": {
  22. "source": {
  23. "match": {
  24. "{{field_name}}": "{{suggestion}}"
  25. }
  26. }
  27. },
  28. "params": {
  29. "field_name": "title"
  30. },
  31. "prune": true
  32. }
  33. }
  34. }
  35. }
  36. }

响应结果:

  1. "suggest" : {
  2. "simple_phrase" : [
  3. {
  4. "text" : "乌1木q 现 虹",
  5. "offset" : 0,
  6. "length" : 8,
  7. "options" : [
  8. {
  9. "text" : "乌鲁木齐 现 虹",
  10. "highlighted" : "<em>乌鲁木齐</em> 现 虹",
  11. "score" : 0.008279981,
  12. "collate_match" : true
  13. }
  14. ]
  15. }
  16. ]
  17. }

其实Term suggesterPhrase suggester对于中文的支持并不好,这也是因为中文所有的词都是靠可分割的字组成的。这是中文的特性。
下面列举一下Phrase suggester可以用到的参数

字段 作用
field 用于作基于语言模式的连词查找字段,这个推荐器将会使用这个字段去获取修正分数的统计数据,这个字段是强制的
gram_size 设置在field中连词的最大数值,如果这个字段不包含连词应该可以被忽略或者直接设置为1,注意ES会尝试基于特定的field字段检测连词的长度,这个字段用了shingle过滤器,如果没有显式指定那它的gram_size将会被设置为max_shingle_size
real_word_error_likelihood 表示一个术语被拼错的甚至存在于字典中的可能性,默认是0.95对应于5%是真实单词是拼写错误(它的意思是我们插入ES中的数据也不是都是正确的,有5%是错误的)
confidence 信任级别定义了一个影响为其他推荐词的阈值输入短语分数的因素,只有分数大于阈值的候选词会包含在结果中,比如设置为1.0将会返回分数大于输入短语的推荐词(这也是默认值),如果设置为0将会返回前N个候选词
max_errors 为形成更正而被认为是拼写错误的术语的最大百分比。该方法接受范围为[0..1)的浮点值作为实际查询项的一部分,或者接受数字>=1作为查询项的绝对数量。默认设置为1.0,这意味着最多只返回一个拼写错误的术语。注意,将其设置得过高会对性能产生负面影响。建议使用1或2这样的低值;否则,花费在建议调用上的时间可能会超过执行查询所花费的时间
separator 用于分隔双字母字段中的术语的分隔符。如果没有设置,将会默认使用空格字符用作分隔符。
size 同(Term Suggester)
analyzer 同(Term Suggester)
shard_size 同(Term Suggester)
text 同(Term Suggester)
highlight 设置高亮推荐,如果不提供该参数将不会有返回highlighted字段;如果提供参数,那必须明确包含pre_tag和post_tag标签(用于包裹高亮内容),如果多个标记连续改变整个短语的改变标记包装而不是每个标记
collate 对于每一个推荐词都做一次query查询,删除推荐词不存在匹配文档的索引,推荐词的校对查询仅仅可以在从本地碎片中获取的推荐词的碎片上执行,query参数必须指定,而且它可以被模板化,当前的推荐词可以作为{{suggestion}}自动使用(可以使用在查询中)。你还可以指定自己的模板params——suggestion值将被添加到指定的变量中,此外如果将返回所有推荐短语还可以指定一个prune来控制,当设置为true时推荐词将有一个额外的参数collate_match,如果相应的短语匹配到文档则为true,否则为false,prune默认值是false

平滑化模式(Smoothing Models)
Phrase suggester支持通过多种平滑模型以平衡低频短语(没有出现在索引中的短语)和高频连词(在索引中至少出现过一次)的权重,参数有:

  • **stupid_backoff**:一个简单的补偿模型,如果高阶数为0和折扣低阶语法模型常数因子,补偿作用将会发挥,默认discount是0.4,stupid_backoff是默认使用的模型
  • **laplace**:一个平滑模型,使用额外的平滑将常数添加所有以平衡权重,默认alpha是0.5
  • **linear_interpolation**:是一种基于用户提供的权值(lambdas)对一元、二元和三元图取加权平均值的平滑模型,线性插值模式没有提供任何默认值,要使用这种模型必须手动提供所有参数(包括trigram_lambda、bigram_lambda、unigram_lambda)

    候选词生成器(Candidate Generators)

    Phrase suggester使用候选生成器生成给定文本中每个项可能的项的列表。单个候选生成器类似于为文本中的每个单独的调用**term suggester**。生成器的输出随后与建议候选项中的候选项结合打分。目前只支持一种候选生成器,即**direct_generator**。建议API接受密钥直接生成器下的生成器列表;列表中的每个生成器都按原始文本中的每个项调用。

直接生成器(Direct Generators)
直接生成器支持以下参数:

字段 作用
**field** 要从中获取候选建议的字段。这是必需的选项,需要全局设置或按建议设置
**size** 每个建议文本标记返回的最大更正
**suggest_mode** 建议模式控制控制文本术语在什么情况下应该被建议。可以指定三个可能的值
1. missing: 仅对索引中没有的建议文本术语提供建议。这是默认值
2. popular: 只建议出现在比原始建议文本术语更多的文档中的建议
3. always: 根据建议文本中的术语建议任何匹配的建议
**max_edits** 表示被选为建议的edit distance的最大值,只能是1,2之间的数(可以是小数),默认是2,其他值会报错
**prefix_length** 表示被选为建议的最小前缀字符的长度,默认为1,增加这个长度可以提高拼写检查的性能,通常拼写错误不会发生在术语的最前面(prefix_len参数已经丢弃)
**min_word_length** 表示推荐文本的最小长度,默认为4(min_word_len参数已被丢弃)
**max_inspections** 表示一个因子,这个参数和shard_size参数相乘以便在碎片级别检查更多的候选者的拼写错误,可以提高以性能为代价的准确性,参数默认为5
**min_doc_freq** 表示一个建议中应包含文档数目的最小限制,可以指定为一个确切的数或文档数的相对百分比,可以通过推荐高频术语来提高质量,==min_doc_freq==默认是0(即不开启此功能),如果将这个参数指定为一个大于1的值(不能是小数,当大于1时不能指定为小数),碎片级别的文档频率用于这个选项
**max_term_freq** 表示推荐文本可以包含的文档数目的最大限制,可以是一个代表文档频率的确切值,也可以是一个相对百分数(比如0.4),如果指定为一个大于1的数(不可以指定为小数),默认是0.01f,这个参数可以用来排除高频术语的拼写检查,高频术语通常在前几个字符是拼写正确的以提高拼写检查的性能,碎片(分片)性能文档频率用于这个选项
**pre_filter** 应用于传递给此候选生成器的每个令牌的筛选器(analyzer)。在生成候选标记之前,此筛选器将应用于原始标记。
**post_filter** 一个过滤器(analyzer),在将生成的令牌传递给实际的短语评分器之前应用于它们。

completion suggester

**Completion Suggester**提供自动完成/随类型搜索的功能。这是一种导航特性,可以在用户键入时引导他们找到相关结果,提高搜索精度。它不是用来纠正拼写或做你想说的功能,如**Term Suggester****Phrase Suggester**
在理想情况下,自动完成功能应该和用户输入一样快,以便为用户已经输入的内容提供即时反馈。因此,**Completion Suggester**在速度上进行了优化。建议器使用支持快速查找的数据结构,但是构建成本很高,并且存储在内存中。

在我们输入相应的关键词时,百度会自动帮助我们不全相关信息,这样不仅方便用户,而且提高了搜索的精度。

接下来然我们看看ES的Completion Suggester怎么做到上面的效果。
在我们最开始初始化数据时,设置了title的附加字段分别为kwpy,而且类型为completion,现在就是使用它们的时候了。
api例子:

  1. GET news/_search
  2. {
  3. "suggest": {
  4. "YOUR_SUGGESTION_KW": {
  5. "prefix": "鄱",
  6. "completion": {
  7. "field": "title.kw"
  8. }
  9. },
  10. "YOUR_SUGGESTION_PY": {
  11. "prefix": "鄱",
  12. "completion": {
  13. "field": "title.py"
  14. }
  15. }
  16. }
  17. }

响应结果:

  1. "suggest" : {
  2. "YOUR_SUGGESTION_KW" : [
  3. {
  4. "text" : "鄱",
  5. "offset" : 0,
  6. "length" : 1,
  7. "options" : [
  8. {
  9. "text" : "鄱阳湖",
  10. "_index" : "news",
  11. "_type" : "_doc",
  12. "_id" : "6",
  13. "_score" : 1.0,
  14. "_source" : {
  15. "title" : "鄱阳湖",
  16. "body" : "鄱阳湖,古称彭蠡、彭蠡泽、彭泽,位于江西省北部,地处九江、南昌、上饶三市,是中国第一大淡水湖,也是中国第二大湖,仅次于青海湖"
  17. }
  18. }
  19. ]
  20. }
  21. ],
  22. "YOUR_SUGGESTION_PY" : [
  23. {
  24. "text" : "鄱",
  25. "offset" : 0,
  26. "length" : 1,
  27. "options" : [
  28. {
  29. "text" : "鄱阳湖",
  30. "_index" : "news",
  31. "_type" : "_doc",
  32. "_id" : "6",
  33. "_score" : 1.0,
  34. "_source" : {
  35. "title" : "鄱阳湖",
  36. "body" : "鄱阳湖,古称彭蠡、彭蠡泽、彭泽,位于江西省北部,地处九江、南昌、上饶三市,是中国第一大淡水湖,也是中国第二大湖,仅次于青海湖"
  37. }
  38. }
  39. ]
  40. }
  41. ]
  42. }

根据前缀就可以返回相应的结果,这里或许让小伙伴有一个疑问,**title.kw**使用的分析器(analyzer)是**keyword**。发送prefix="鄱",可以匹配鄱阳湖这是没有问题的,但是为什么**title.py**也能匹配到呢?**title.py**不是应该输入拼音才能匹配到吗?

其实是这样的,Completion Suggester也是会将发送过来的短句进行分析的。默认使用的分析器就是指定字段所使用的分析器,title.py使用的分析器是pinyin,所以prefix=”鄱”会被分析成po然后再进行前缀匹配。就能匹配到鄱阳湖的拼音了。

  1. GET news/_search
  2. {
  3. "suggest": {
  4. "YOUR_SUGGESTION_PY": {
  5. "prefix": "破杨虎",
  6. "completion": {
  7. "field": "title.py"
  8. }
  9. }
  10. }
  11. }

因为没有安装拼音分词器,所以此处需要参考原文档

下面列举一下Completion Suggester支持的参数:

参数名称 作用
size 返回建议的数量(默认为5)
skip_duplicates 是否应该过滤重复的建议(默认为false)
field 要在其上运行查询的字段名(必需的)

查询可以返回来自不同文档的重复建议。可以通过将skip duplicates设置为true来修改此行为。设置后,此选项将从结果中过滤出具有重复建议的文档

  1. POST news/_doc/7
  2. {
  3. "title": "鄱阳湖",
  4. "body": "我家在鄱阳湖"
  5. }
  6. POST news/_search
  7. {
  8. "suggest": {
  9. "YOUR_SUGGESTION_1": {
  10. "text": "鄱阳",
  11. "completion": {
  12. "field": "title.kw"
  13. }
  14. },
  15. "YOUR_SUGGESTION_2": {
  16. "text": "鄱阳",
  17. "completion": {
  18. "field": "title.kw",
  19. "skip_duplicates": true
  20. }
  21. }
  22. }
  23. }

响应结果:

  1. "suggest" : {
  2. "YOUR_SUGGESTION_1" : [
  3. {
  4. "text" : "鄱阳",
  5. "offset" : 0,
  6. "length" : 2,
  7. "options" : [
  8. {
  9. "text" : "鄱阳湖",
  10. "_index" : "news",
  11. "_type" : "_doc",
  12. "_id" : "6",
  13. "_score" : 1.0,
  14. "_source" : {
  15. "title" : "鄱阳湖",
  16. "body" : "鄱阳湖,古称彭蠡、彭蠡泽、彭泽,位于江西省北部,地处九江、南昌、上饶三市,是中国第一大淡水湖,也是中国第二大湖,仅次于青海湖"
  17. }
  18. },
  19. {
  20. "text" : "鄱阳湖",
  21. "_index" : "news",
  22. "_type" : "_doc",
  23. "_id" : "7",
  24. "_score" : 1.0,
  25. "_source" : {
  26. "title" : "鄱阳湖",
  27. "body" : "我家在鄱阳湖"
  28. }
  29. }
  30. ]
  31. }
  32. ],
  33. "YOUR_SUGGESTION_2" : [
  34. {
  35. "text" : "鄱阳",
  36. "offset" : 0,
  37. "length" : 2,
  38. "options" : [
  39. {
  40. "text" : "鄱阳湖",
  41. "_index" : "news",
  42. "_type" : "_doc",
  43. "_id" : "6",
  44. "_score" : 1.0,
  45. "_source" : {
  46. "title" : "鄱阳湖",
  47. "body" : "鄱阳湖,古称彭蠡、彭蠡泽、彭泽,位于江西省北部,地处九江、南昌、上饶三市,是中国第一大淡水湖,也是中国第二大湖,仅次于青海湖"
  48. }
  49. }
  50. ]
  51. }
  52. ]
  53. }

当设置为真时,这个选项会降低搜索速度,因为需要访问更多的建议才能找到顶部N

模糊搜索(Fuzzy queries)

完成建议器也支持模糊查询,这意味着你可以在你的搜索中有一个错误,但仍然可以得到结果

  1. POST news/_search
  2. {
  3. "suggest": {
  4. "YOUR_SUGGESTION": {
  5. "text": "鄱a",
  6. "completion": {
  7. "field": "title.kw",
  8. "fuzzy": {
  9. "fuzziness": 1
  10. }
  11. }
  12. }
  13. }
  14. }
  15. POST news/_search
  16. {
  17. "suggest": {
  18. "YOUR_SUGGESTION": {
  19. "text": "1鄱1",
  20. "completion": {
  21. "field": "title.kw",
  22. "skip_duplicates": true,
  23. "fuzzy": {
  24. "prefix_length": 0,
  25. "fuzziness": 2
  26. }
  27. }
  28. }
  29. }
  30. }

与查询前缀共享最长前缀的建议得分更高;
模糊查询可以取特定的模糊参数。支持以下参数:

参数名称 作用
fuzziness 模糊因子,默认为自动。有关允许的设置,请参阅模糊
transpositions 如果设置为真,转换将被视为一个更改,而不是两个,默认值为真
min_length 返回模糊建议之前输入的最小长度,默认为3
prefix_length 输入的最小长度默认为1,不检查模糊选项
unicode_aware 如果为真,那么所有度量(如模糊编辑距离、换位和长度)都是以Unicode代码点而不是字节来度量的。这比原始字节稍微慢一些,因此默认将其设置为false

Elasticsearch:fuzzy 搜索 (模糊搜索)
正则查询(Regex queries)

完成提示符还支持正则表达式查询,这意味着可以将前缀表示为正则表达式

  1. POST news/_search
  2. {
  3. "suggest": {
  4. "YOUR_SUGGESTION": {
  5. "regex": "[p|w]y",
  6. "completion": {
  7. "field": "title.py"
  8. }
  9. }
  10. }
  11. }
  1. POST news/_search
  2. {
  3. "suggest": {
  4. "YOUR_SUGGESTION": {
  5. "regex": "[p|w]",
  6. "completion": {
  7. "field": "title.py"
  8. }
  9. }
  10. }
  11. }

当我们不仅需要自动完成,还需要将最新的热点信息排在上面,而不是按长度去排列怎么办。Completion suggester也是可以帮助我们搞定的。
要实现那个需求其实就是要我们能自定义建议的权重
链接: ES 搜索建议-权重