基于Elasticsearch实现搜索建议

搜索建议是搜索的一个重要组成部分,一个搜索建议的实现一般须要考虑建议词的来源、匹配、排序、聚合、关联的文档数和拼写纠错等,本文介绍一个基于Elasticsearch实现的搜索建议。html

问题描述

电商网站的搜索是最基础最重要的功能之一,搜索框上面的良好体验能为电商带来更高的收益,咱们先来看看淘宝、京东、亚马逊网站的搜索建议。数据库

在淘宝的搜索框输入【卫衣】时,下方的搜索建议包括建议词以及相关的标签:
淘宝的搜索建议缓存

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

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

经过上述对比能够看出,不一样的电商对于搜索建议的侧重点略有不一样,但核心的问题包括:网站

  • 匹配:可以经过用户的输入进行前缀匹配;
  • 排序:根据建议词的优先级进行排序;
  • 聚合:可以根据建议词关联的商品进行聚合,好比聚合分类、聚合标签等;
  • 纠错:可以对用户的输入进行拼写纠错;

搜索建议实现

在咱们的搜索建议实现里,主要考虑了建议词的来源、匹配、排序、关联的商品数量和拼写纠错。ui

SuggestionDiscovery

  • SuggestionDiscovery的职责是发现建议词;
  • 建议词的来源能够是商品的分类名称、品牌名称、商品标签、商品名称的高频词、热搜词,也能够是一些组合词,好比“分类 + 性别”和“分类 + 标签”,还能够是一些自定义添加的词;
  • 建议词维护的时候须要考虑去重,好比“卫衣男”和“卫衣 男”应该是相同的,“Nike”和“nike”也应该是相同的;
  • 因为建议词的来源一般比较稳定,因此执行的周期能够比较长一点,好比每周一次;

SuggestionCounter

  • SuggestionCounter的职责是获取建议词关联的商品数量,若是须要能够进行一些聚合操做,好比聚合分类和标签;
  • SuggestionCounter的实现的时候因为要真正地调用搜索接口,应该尽可能避免对用户搜索的影响,好比在凌晨执行而且使用单线程调用;
  • 为了提高效率,应该使用Elasticsearch的Multi Search接口批量进行count,同时批量更新数据库里建议词的count值;
  • 因为SuggestionCounter是比较耗资源的,能够考虑延长执行的周期,可是这可能会带来count值与实际搜索时偏差较大的问题,这个须要根据实际状况考虑;

SuggestionIndexRebuiler

  • SuggestionIndexRebuiler的职责是负责重建索引;
  • 考虑到用户的搜索习惯,可使用Multi-fields来给建议词增长多个分析器。好比对于【卫衣 套头】的建议词使用Multi-fields增长不分词字段、拼音分词字段、拼音首字母分词字段、IK分词字段,这样输入【weiyi】和【套头】均可以匹配到该建议词;
  • 重建索引时经过是经过bulk批量添加到临时索引中,而后经过别名来更新;
  • 重建索引的数据依赖于SuggestionCounter,所以其执行的周期应该与SuggestionCounter保持一致;

SuggestionService

  • SuggestionService是真正处于用户搜索建议的服务类;
  • 一般的实现是先到缓存中查询是否能匹配到缓存记录,若是能匹配到则直接返回;不然的话调用Elasticsearch的Prefix Query进行搜索,因为咱们在重建索引的时候定义了Multi-fields,在搜索的时候应该用boolQuery来处理;若是此时Elasticsearch返回不为空的结果数据,那么加入缓存并返回便可;
POST /suggestion/_search
{
  "from" : 0,
  "size" : 10,
  "query" : {
    "bool" : {
      "must" : {
        "bool" : {
          "should" : [ {
            "prefix" : {
              "keyword" : "卫衣"
            }
          }, {
            "prefix" : {
              "keyword.keyword_ik" : "卫衣"
            }
          }, {
            "prefix" : {
              "keyword.keyword_pinyin" : "卫衣"
            }
          }, {
            "prefix" : {
              "keyword.keyword_first_py" : "卫衣"
            }
          } ]
        }
      },
      "filter" : {
        "range" : {
          "count" : {
            "from" : 5,
            "to" : null,
            "include_lower" : true,
            "include_upper" : true
          }
        }
      }
    }
  },
  "sort" : [ {
    "weight" : {
      "order" : "desc"
    }
  }, {
    "count" : {
      "order" : "desc"
    }
  } ]
}
  • 若是Elasticsearch返回的是空结果,此时应该须要增长拼写纠错的处理(拼写纠错也能够在调用Elasticsearch搜索的时候带上,可是一般状况下用户并无拼写错误,因此建议仍是在后面单独调用suggester);若是返回的suggest不为空,则根据新的词调用建议词服务;好比用户输入了【adidss】,调用Elasticsearch的suggester获取到的结果是【adidas】,则再根据adidas进行搜索建议词处理。
POST /suggestion/_search
{
  "size" : 0,
  "suggest" : {
    "keyword_suggestion" : {
      "text" : "adidss",
      "term" : {
        "field" : "keyword",
        "size" : 1
      }
    }
  }
}
  • 关于排序:在咱们的实现里面是经过weight和count进行排序的,weight目前只考虑了建议词的类型(好比分类 > 品牌 > 标签);

实现效果和后续改进

  • 经过上面的实现,咱们已经能实现一个比较强大的搜索建议词了,实际的效果以下所示:

最终效果

  • 后续能够考虑的改进:参考亚马逊增长分类的聚合展现、增长用户个性化的处理支持更好的建议词排序、基于用户的搜索历史支持更好的建议词推荐;

参考资料

相关文章
相关标签/搜索