elasticsearch5.2搜索建议(自动补全)

问题描述

电商网站的搜索是最基础最重要的功能之一,搜索框上面的良好体验能为电商带来更高的收益,我们先来看看淘宝、京东、亚马逊网站的搜索建议。
在淘宝的搜索框输入【卫衣】时,下方的搜索建议包括建议词以及相关的标签:

Paste_Image.png

在京东的搜索框输入【卫衣】时,下方搜索建议右方显示建议词关联的商品数量:

Paste_Image.png

在亚马逊的搜索框输入【卫衣】时,搜索建议上部分能支持在特定的分类下进行搜索:

Paste_Image.png
通过上述对比可以看出,不同的电商对于搜索建议的侧重点略有不同,但核心的问题包括:
  1. 建议词的来源可以是商品的分类名称、品牌名称、商品属性、商品名称的高频词、热搜词,也可以是一些组合词,比如“分类 + 性别”和“分类 + 属性”,还可以是一些自定义添加的词;
  2. 建议词维护的时候需要考虑去重,比如“Nike”和“nike”应该是相同的;
关键词索引映射:
curl -XPUT http://192.168.138.210:9200/keyword/ -d'
{
    "settings" : {
        "analysis" : {
            "analyzer" : {
                "first_py_letter_analyzer" : {
                    "tokenizer" : "first_py_letter",
                    "filter":"edgeNGram_filter"
                },
                "full_pinyin_letter_analyzer" : {
                    "tokenizer" : "full_pinyin_letter",
                    "filter":"edgeNGram_filter"
                },
                "edgeNGram_analyzer":{
                    "tokenizer" : "edgeNGram_tokenizer"
                }
            },
            "tokenizer" : {
                "first_py_letter" : {
                    "type" : "pinyin",
                    "keep_first_letter" : true,
                    "keep_full_pinyin" : false,
                    "keep_original" : false,
                    "limit_first_letter_length" : 16,
                    "lowercase" : true,
                    "trim_whitespace" : true,
                    "keep_none_chinese_in_first_letter": false,
                    "none_chinese_pinyin_tokenize": false,
                    "keep_none_chinese": true,
                    "keep_none_chinese_in_joined_full_pinyin": true
                },
                "full_pinyin_letter" : {
                    "type": "pinyin",
                    "keep_separate_first_letter": false,
                    "keep_full_pinyin": false,
                    "keep_original": false,
                    "limit_first_letter_length": 16,
                    "lowercase": true,
                    "keep_first_letter": false,
                    "keep_none_chinese_in_first_letter": false,
                    "none_chinese_pinyin_tokenize": false,
                    "keep_none_chinese": true,
                    "keep_joined_full_pinyin": true,
                    "keep_none_chinese_in_joined_full_pinyin": true
                },
                "edgeNGram_tokenizer":{
                    "type": "edgeNGram",
                    "min_gram": 1,
                    "max_gram": 15,
                    "token_chars": ["letter", "digit"]
                }
            },
            "filter":{
                "edgeNGram_filter":{
                    "type": "edgeNGram",
                    "min_gram": 1,
                    "max_gram": 50,
                    "token_chars": ["letter", "digit"]
                }               
            }
        },
        "number_of_shards": 5,
        "number_of_replicas": 1
    },
    "mappings":{
        "suggestion":{
            "properties": {
                "keyword": {
                    "type": "keyword",
                    "fields": {
                        "keyword_ik": {
                            "type": "text",
                            "analyzer": "edgeNGram_analyzer"
                        },
                        "keyword_pinyin": {
                            "type": "text",
                            "analyzer": "full_pinyin_letter_analyzer"
                        },
                        "keyword_first_py": {
                            "type": "text",
                            "analyzer": "first_py_letter_analyzer"
                        }
                    }
                },              
                "count": {
                    "type": "long",
                    "index": "not_analyzed"
                },
                "weight": {
                    "type": "integer",
                    "index": "not_analyzed"
                }
            }
        }        
    }
}'

搜索语句:

{
    "sort": [
        {
            "weight": "desc"
        },
        {
            "count": "desc"
        },
        {
            "_score": "desc"
        }
    ],
    "query": {
        "dis_max": {
            "queries": [
                {
                    "match_phrase": {
                        "keyword.keyword_ik": {
                            "query": "卫衣"
                        }
                    }
                },
                {
                    "match_phrase": {
                        "keyword.keyword_pinyin": {
                            "query": "卫衣",
                            "boost": 2
                        }
                    }
                }
            ],
            "tie_breaker": 1
        }
    }
}

如果Elasticsearch返回的是空结果,此时应该需要增加拼写纠错的处理(拼写纠错也可以在调用Elasticsearch搜索的时候带上,但是通常情况下用户并没有拼写错误,所以建议还是在后面单独调用suggester);如果返回的suggest不为空,则根据新的词调用建议词服务;比如用户输入了【adidss】,调用Elasticsearch的suggester获取到的结果是【adidas】,则再根据adidas进行搜索建议词处理。

{
    "suggest": {
        "keyword_suggestion": {
            "text": "adidss",
            "phrase": {
                "field": "keyword.keyword_pinyin",
                "size": 1,
                "analyzer":"standard"
            }
        }
    }
}

关于排序:在我们的实现里面是通过weight和count进行排序的,weight目前只考虑了建议词的类型(比如分类 > 品牌 > 属性);

下面为测试数据:


Paste_Image.png

下面输入错误的关键词“adidss”,获得的结果是:


Paste_Image.png

到此搜索建议基本上完成,但我估计大家会有个疑惑,因为针对自动补全,elasticsearch 提供了 completion suggestion 来解决,但为什么我没用呢?主要是 completion suggestion 不支持按字段来排序,比如我这需求就是要按关键词权重(weight)和搜索次数(count)来排序的

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 173,420评论 25 708
  • 在基于Elasticsearch实现搜索建议一文中我们曾经介绍过如何基于Elasticsearch来实现搜索建议,...
    ginobefun阅读 17,393评论 2 48
  • 经常看到有人在讨论,婚姻是爱情的坟墓,进来的人想出去。可我进来了,就不想走了。 那是12年大学开始实...
    分必久何阅读 529评论 4 3
  • 有很多时候当我们遇到窘境或难题都会感觉困惑和焦虑。假如我们把它当做一个有型物体,处于困局的时候应该仅仅是看到这个物...
    泡泡里的鱼阅读 291评论 0 0
  • "我也不想说出那些让人难受的话,可是我控制不住我自己" 一直以来我都很关注一个话题,就是语言暴力。可能很多人不知道...
    林深见鹿723阅读 1,374评论 0 1