商品搜索分为三大模块
1 数据同步ES
1.1 ElasticSearch全文检索
- 百度,淘宝为什么查询快?
都采用了ES全文检索技术。
- 全文检索技术,查询快的原因?
ES使用倒排索引,对搜索内容进行分词,一个词对应多个id。当然,生成的倒排索引中,词条会排序,形成一颗树形结构,提升词条的查询速度。输入关键词之后就会很快的根据分词找到对应的id便于查到数据。
- 为什么有了mysql关系数据库,还需要引入ES?
性能低,功能弱,例如:模糊查询时不走索引会极其的慢。
1.2 商品上下架同步到ES
实现思路
- canal微服务监听tb_spu表,当上架(is_marketable变为1),拿到spuId根据routingKey分发消息给绑定的queue。
- search微服务监听此queue,拿到spuId,业务层里feign调用拿到对应的skuList,同步ES数据
具体操作
- canal模块中:发送消息到mq,只要tb_spu表发生改变,就有消息
- search模块中:
- 引入goods_api(
当中创建了索引结构,实体类skuInfo中加各个注解
)
- 配置yml
- 定义一个ESManagerMapper继承ElasticsearchRepository<SkuInfo,Long>
- 编写监听类,拿到spuId调用业务层
- 通过elasticsearchTemplate根据SkuInfo创建ES的索引和映射
- 业务层feign调用拿到skuList,先转成字符串再转成对应的
List<SkuInfo>
(这个设计非常巧妙,因为sku和skuInfo的字段相同,但属性不一,通过JSON转换一下即可),遍历每个skuInfo,将规格spec转成specMap,最后调用saveAll方法将所有的上架商品同步到ES。
public void importDataTemp(String spuId) {
List<Sku> skuList = skuFeign.findSkuListBySpuId(spuId);
if (skuList==null||skuList.size()<=0){
throw new BusinessException(new Result(false, StatusCode.REMOTEERROR,"未查到相关Sku信息"));
}
String jsonString = JSON.toJSONString(skuList);
List<SkuInfo> skuInfos = JSON.parseArray(jsonString, SkuInfo.class);
for (SkuInfo skuInfo : skuInfos) {
Map specMap = JSON.parseObject(skuInfo.getSpec(), Map.class);
skuInfo.setSpecMap(specMap);
}
esManagerMapper.saveAll(skuInfos);
}
- 下架操作雷同上架
先通过spuId拿到sku集合,最后遍历删除esManagerMapper.deleteById(Long.parseLong(sku.getId()));
2 商品搜索条件封装
2.1 基本分为6个条件
- 根据搜索关键字查询
- 规格过滤
- 价格区间搜索
- 分页查询
- 排序查询
- 高亮查询
2.2 步骤
public interface SearchService {
public Map search(Map<String, String> paramMap) throws Exception;
}
- 构造查询条件封装对象new NativeSearchQueryBuilder()
- 组合查询条件使用BoolQuery
- 关键字查询:must连接方式,使用matchQuery(ik分词器进行分词),指定es中的字段和map中的keywords对应
- 品牌和规格聚合:addAggregation
- 品牌过滤:filter连接方式,使用teamQuery,指定es中的brandName和map中的brand对应
- 规格过滤:filter连接方式,使用teamQuery
- 价格区间过滤:filter连接方式,使用rangeQuery(lte小于,gte大于)
- 分页过滤:withPageable
- 价格过滤:withSort
- 高亮过滤:withHighlightFields
- 调用elasticsearchTemplate的queryForPage
- 最后将结果一一封装到返回的结果集(resultMap中)
@Override
public Map search(Map<String, String> searchMap) {
Map<String, Object> resultMap = new HashMap<>();
if (searchMap != null && searchMap.size()>0) {
NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder