前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >为商品搜索添加联想输入与热门搜索推荐功能

为商品搜索添加联想输入与热门搜索推荐功能

作者头像
吴就业
发布2021-04-02 00:31:46
1.6K0
发布2021-04-02 00:31:46
举报
文章被收录于专栏:Java艺术Java艺术

无论是新闻、内容、还是电商平台,联想输入已经成为搜索功能的标配,早已不是什么新鲜事物。我们随便打开一个搜索引擎或者是电商平台,当我们在输入框输入拼音或者文字时就会看到输入框下方弹出有意义的搜索建议,提示我们是不是想要输入“以下”内容,帮助我们补齐输入或是修正错误的输入,优化我们的搜索体验。

搜索引擎的另一个标配就是热门搜索推荐,让用户能够了解到当下大家最关心的新闻、商品或是八卦,在用户不知道想要搜索什么的时候,给用户一些选择,优化用户体验,实际上也是达到广告的效果。

当然,笔者不是产品经理,所以我们不分析联想输入与热门搜索推荐功能背后的意义,仅从技术角度介绍大家最感兴趣的技术问题,即如何为搜索添加联想输入搜索与热门搜索推荐功能,这也是笔者最近接到的一个需求。

本篇内容介绍如何基于ElasticSearch为商品搜索添加联想输入与热门搜索推荐功能。

为商品搜索添加热门搜索推荐

实现热门搜索推荐最简单的方式就是在用户点击搜索时记录用户输入的文本内容,然后为前端提供一个接口,用于统计所有用户输入的文本内容取出出现次数最多的前几条记录响应给前端。当然,最好加上搜索时间,统计时加上时间范围过滤,只关心最近一段时间的数据。

创建搜索日记索引

假设我们创建的商品搜索日记索引名称为search_log

  • Kibana中调用接口

PUT /search_log

{
    "mappings":{
        "properties":{
            "keyword":{
                "type":"keyword",
                "ignore_above":256
            },
            "search_time":{
                "type":"long"
            }
        }
    },
    "settings": {
        "index": {
          "number_of_shards": 5,
          "number_of_replicas": 3
        }
    }
}

search_log索引中,我们为keyword字段设置类型为keyword类型。

  • Text类型:查询时先分词再索引,支持模糊查询,不支持聚合;
  • keyword类型:查询时不进行分词,直接索引,支持模糊查询,支持聚合。

查询热门搜索关键词

  • Kibana中调用接口

GET /search_log/_search

{
  "query": {
    "bool": {
      "filter": [
        {
          "range": {
            "search_time": {
              "gte": 1000000000000,
              "lte": 10000000000000
            }
          }
        }
      ]
    }
  },
  "aggs": {
    "my_name": {
      "terms": {
        "field": "keyword",
        "size": 1
      }
    }
  }
}
  • Java代码实现,聚合搜索获取出现次数最多的前几条记录
@Service
public class GoodsSearchService{
    @Override
    public List<String> popularSearch(int n) {
       SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
       searchSourceBuilder.size(0);
       searchSourceBuilder.query(QueryBuilders.boolQuery()
             .filter(QueryBuilders.rangeQuery("search_time")
                    .gte(System.currentTimeMillis() - (1000L * 31 * 24 * 60 * 60))));
       searchSourceBuilder.aggregation(AggregationBuilders.terms("group_by_keyword")
                        .field("keyword").size(n));
       try {
           SearchRequest searchRequest = new SearchRequest("search_log");
           searchRequest.source(searchSourceBuilder);
           SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
           Aggregations aggregations = searchResponse.getAggregations();
           Terms trem = aggregations.get("group_by_keyword");
           List<Terms.Bucket> buckets = (List<Terms.Bucket>) trem.getBuckets();
           List<String> hotWords = new ArrayList<>();
           for (Terms.Bucket bucket : buckets) {
               String key = (String) bucket.getKey();
               hotWords.add(key);
           }
           return hotWords;
       } catch (IOException e) {
           e.printStackTrace();
           return Collections.emptyList();
       }
   }
}

为商品搜索添加联想输入

笔者在做这个功能的时候也有过一个疑问:这个功能应该是放在前端实现呢,还是后端实现呢?

为解答这个问题,笔者去看了谷歌搜索、百度搜索以及淘宝搜索,发现这些搜索引擎都是通过请求后端接口实现的。

谷歌搜索:

百度搜索:

淘宝搜索:

很好理解,以淘宝搜索为例,因为前端没有商品,联想输入提示需要知道商品库里有没有商品名称以用户输入的字符串为前缀的商品,总不能给用户提示搜索不存在的商品,而且也还需要根据匹配成绩实现排序。

我们是基于ElasticSearch提供的Completion Suggest实现的联想输入提示功能。只需要在创建索引时为字段加上如下属性:

"fields":{
   "suggest":{
   "type":"completion",
   "analyzer":"ik_max_word"
}

例如给商品索引的商品名称字段添加Suggest,类型选择Completion ,则配置如下:

"goodsName":{
   "search_analyzer":"ik_smart",
   "analyzer":"ik_max_word",
   "type":"text",
   "fields":{
       "suggest":{
           "type":"completion",
           "analyzer":"ik_max_word"
        }
    }
}

注意:如果索引已经存在了就没有那么简单了,ElasticSearch不支持修改索引字段,所以只能通过reindex修改索引字段信息。

通过reindex修改索引字段信息

  • 第一步:创建新的索引

PUT /新索引名称

{
  "mappings": {
    "properties": {
      // 其它字段省略
      "goodsName": {
        "search_analyzer": "ik_smart",
        "analyzer": "ik_max_word",
        "type": "text",
        "fields": {
          "suggest": {
            "type": "completion",
            "analyzer": "ik_max_word"
          }
        }
      }
    }
  },
  "settings":{
    "index":{
      "number_of_shards":5,
      "number_of_replicas":3
    }
  }
}
  • 第二步:执行reindex操作

当数据量非常大时,reindex耗时非常长,因此需要在reindex请求url加上wait_for_completion=false参数让reindex异步执行。

提示:我们测试环境商品库五千多万的数据,在reindex十几分钟后由于内存不足最终把ElasticSearch搞崩了,而且也只同步了九百多万条商品记录,因此reindex一定要非常小心操作,根据索引的文档数量和可用内存做好风险评估。

POST _reindex?wait_for_completion=false

{
  "source": {
    "index": "旧索引名称"
  },
  "dest": {
    "index": "新索引名称"
  }
}

等待两个索引的记录总数相同时再进行下一步操作,即等待以下两个请求返回相同的结果: GET /新索引名称/_count GET /旧索引名称/_count

  • 第三步:删除旧的索引

DELETE /旧索引名称

  • 第四步:创建别名,别名与旧索引名称相同,然后让别名指向新索引

POST _aliases

{
  "actions": [
    {
      "add": {
        "index": "新索引名称",
        "alias": "旧索引名称"
      }
    }
  ]
}

使用前缀进行联想词查询

  • Kibana中调用接口

GET /索引名称/_search

{
  "size": 0, 
  "suggest": {
    "goodsNameSuggest": {
      "prefix":"BOBO",
      "completion": {
        "field": "goodsName.suggest",
        "size":1
      }
    }
  }
}
  • 使用Java代码实现
@Service
public class GoodsSearchService{
    @Override
    public List<String> lenovoSearch(String inputStr) {
        CompletionSuggestionBuilder suggestion = SuggestBuilders
                .completionSuggestion("goodsName.suggest")
                .prefix(inputStr)
                .size(10);
        SuggestBuilder suggestBuilder = new SuggestBuilder();
        suggestBuilder.addSuggestion("mySuggest", suggestion);
        SearchRequest searchRequest = new SearchRequest("商品索引名称");
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.suggest(suggestBuilder);
        searchSourceBuilder.size(0);
        searchRequest.source(searchSourceBuilder);
        try {
            SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
            Suggest suggest = response.getSuggest();
            // 没有任何数据
            if (suggest == null) {
                return Collections.emptyList();
            }
            Suggest suggestResult = response.getSuggest();
            Suggest.Suggestion<? extends Suggest.Suggestion.Entry<? extends Suggest.Suggestion.Entry.Option>>
                    suggestionResult = suggestResult.getSuggestion("mySuggest");
            List<? extends Suggest.Suggestion.Entry<? extends Suggest.Suggestion.Entry.Option>> list
                    = suggestionResult.getEntries();
            List<String> suggestList = new ArrayList<>();
            if (list == null) {
                return null;
            } else {
                for (Suggest.Suggestion.Entry<? extends Suggest.Suggestion.Entry.Option> e : list) {
                    for (Suggest.Suggestion.Entry.Option option : e) {
                        suggestList.add(option.getText().toString());
                    }
                }
            }
            return suggestList;
        } catch (IOException e) {
            e.printStackTrace();
            return Collections.emptyList();
        }
    }
}

总结

热门搜索推荐功能相对简单,即便不使用ElasticSearch,也可以选择其它的分析型数据库代替,比较难的是联想输入,并且对性能要求高,因为前端每输入一个字或字母都需要请求接口,靠数据库前缀匹配查询无法实现高性能。但看起来非常难实现的功能ElasticSearch都帮我们解决了。

这里推荐一篇文章:《Google 搜索的即时自动补全功能究竟是如何“工作”的?》。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-03-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Java艺术 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 为商品搜索添加热门搜索推荐
    • 创建搜索日记索引
      • 查询热门搜索关键词
      • 为商品搜索添加联想输入
        • 通过reindex修改索引字段信息
          • 使用前缀进行联想词查询
          • 总结
          相关产品与服务
          Elasticsearch Service
          腾讯云 Elasticsearch Service(ES)是云端全托管海量数据检索分析服务,拥有高性能自研内核,集成X-Pack。ES 支持通过自治索引、存算分离、集群巡检等特性轻松管理集群,也支持免运维、自动弹性、按需使用的 Serverless 模式。使用 ES 您可以高效构建信息检索、日志分析、运维监控等服务,它独特的向量检索还可助您构建基于语义、图像的AI深度应用。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档