SpringCloud商城day07 商品搜索-2021-10-12

一.关键词查询

1. 实现思路: 

2. search微服务查询接口: com.changgou.search.service.SearchServiceImpl.java

(1) Map封装返回结果: 分页功能的实现
    Map封装查询条件: 关键字 / 品牌 / 规格 / 销量 / 价格区间..

(2) @Service
    public class SearchServiceImple implemtns SearchServiec{
    
        //注入ES模板类完成查询
        @Autowired
        private ElasticSearchTemplate elasticsearchTemplate; 

      @Override
      public Map search(Map<String, String> searchMap){ 

        //查询结果封装map
        Map<String, String> resultMap = new HashMap<>();

        if(searchMap!=null){
            
            //参数1: 条件对象
            NativeSearchQuery nativesSearchQueryBuilder = new NativeSearchQueryBuilder();
            //拼接查询条件
           BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
            //按照关键字查询
            if(StringUtils.isNotEmpty(searchMap.get("keyword"))){
                //模糊匹配查询 商品name域;值
                boolQuery.must(QueryBuilders.matchQuery("name", searchMap.get("keywords").operator(Operator.AND));
            }
            //封装查询条件
            nativeSearchQueryBuilder.withQuery(boolQuery);


            //参数2: 查询实体类
            //参数3: 结果操作对象
            //开启查询
           AggregatedPage<SkuInfo> resultInfo = elasticsearchTemplate.queryForPage(nativeSearchQueryBuilder(), SkuInfo.class, new SearchResultMapper(){
                //参数3接口的方法重写, 查询结果
                @Override
                public<T> AggregatedPage<T> mapResults(SearchResponse searchResponse, Class<T> aClass, Pageable pageable){
                //结果查询集合
                List<T> list = new ArrayList<>();
                //数据命中
                SearchHits hits = searchResponse.getHits();
                //遍历命中记录, 得到索引库的每一个docs, 即商品
                for(SearchHit hit : hits){
                    //hit数据的json格式转换为skuInfo
                    SkuInfo skuInfo = JSON.parseObject(hit.getSourceAsString(), SkuInfo.class);
                    list.add((T)skuInfo);
                }  
                }
                return new AggregatedPageImpl<T>(list, pageable, hits.getTotalHits(), searchResponse.getAggregation());
                }
                });

                //封装返回结果,总记录数
                resultMap.put("total", resultInfo.getTotalElements()); //总记录数
                resultMap.put("totalPage", resultInfo.getTotalPages()): //总页数
                resultMap.put("rows", resultInfo.getContent());  //数据集合
                return resultMap;
                
        }
        return null;
    }

3. search.controller.SearchController.java

@RestController
@RequestMapping("/search")
public class SearchController{
    @Autowired
    private SearchService searchService;
    
    @GetMapping()
    public Map search(@RequestParam Map<String,String> searchMap){
        
        //特殊符号处理: get请求传参在路径上 -> 值会被编码解码
        this.handleSearchMap(searchMap);
        
        Map searchResult = searchService.search(searchMap);
        //总记录数/页数/数据量
        return searchResult;
    }

    //方法实现
    //对搜索入参带有特殊符号进行处理
    public void handlerSearchMap(Map<String,String> searchMap){

        if(null != searchMap){
            //获取键值对集合
            Set<Map.Entry<String, String>> entries = searchMap.entrySet();
            //遍历
            for (Map.Entry<String, String> entry : entries) {
                //如果以规格名开头, 则转换
                if(entry.getKey().startsWith("spec_")){
                    //转换"+"为 "%2B
                    searchMap.put(entry.getKey(),entry.getValue().replace("+","%2B"));
                }
            }
        }
    }
}

二. 条件筛选

1.  品牌过滤:

(1) 修改 com.changgou.search.service.impl.SearchServiecImpl.java
    //按品牌过滤查询
    if(StringUtils.isNotEmpty(searchMap.get("brand"))){
        //过滤精确查询
        boolQuery.filter(QueryBuilders.termQuery("brandName", searchMap.get("brand")));
    }
    
    //对品牌域分组查询 -> 聚合查询
    String skuBrand = "skuBrand";

    //条件查询对象 -> 聚合(分组(列名).(需要分组的域名))  nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms(skuBrand).field("brandName));
    //封装品牌分组结果 -> 聚合查询的列名
    StringTerms brandTerms = (StringTerms)resultInfo.getAggregation(skuBrand);
    
    //封装数据到集合, 返回给前端 -> 流运算->遍历得到品牌 -> 转换集合保存
    List<String> brandList = brandTerms.getBuckets().stream().map(bucket->bucket.getKeyAsString()).colletct(Collectors.toList());
    //封装到结果map
    resultMap.put("brandList", brandList);
    

三. 规格过滤 -> 数据操作问题

1. 规格信息 -> 多次筛选 -> 约定规格名 -> 前缀 "spec_" -> 提取spec_开头的字段 ->符合则判断为规格信息

2. 操作域的拼接: SpecMap.specName.keyword

(1)修改com.changgou.search.service.SearchServiceImpl.java
    //按照规格进行过滤查询
    for(String key: searchMap.keySet()){
        if(key.startsWith("spec_"){
            //编码解码
            String value = searchMap.get(key).replace("%2B", "+");
            //过滤精确查询 -> 查询(规格域名, 具体指)
            boolQuery.filter(QueryBuilders.termQuery("specMap."+key.subString(5)+".keyword"), value));
        }
    }

(2) 规格也需要分组/聚合查询 -> 随着搜索结果的改变而变化
    修改SearchServiceImpl.java
    //按照规格查询
    String skuSpec= "skuSpec";
    nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms(skuSpec).field("spec.keyword);
    

    //封装规格分组结果
    StringTerms specTerms = (StringTerms)resultInfo.getAggregation(skuSpec);
    List<String> specList = specTerms.getBuckets().stream().map(bucket->bucket.getKeyAsString()).toCollect(Collectors.toList());
    
    resultMap.put("specList", specList);

四. 价格区间搜索 -> 聚合查询

1. 需求: 用户选择价格区间 -> 切割价格区间字符串 -> 得到数组的两个元素 -> 价格区间作为过滤条件 -> 如果切割后的数组长度为1, 条件是>=3000

2.实现:

(1) search微服务添加价格区间过滤方法 com.changgou.search.serivce.impl.SearchServiceImpl.java
    if(StringUtils.isNotEmpty(searchMap.get("price"))){
        String[] prices = searchMap.get("get").split("-");
        //0-500元...
        if(prices.length==2){  //小于等于第二个元素
            boolQuery.filter(QueryBuilders.rangeQuery("price").lte(prices[1]));
        }
        //数组长度不为2, >=3000元, 大于等于第一个元素
        boolQuery.filter(QueryBuilders.rangeQuery("price").gte(prices[0]);
    }

五. 分页查询

1. 前端传递参数(当前页+每页显示数) 

2. 实现:

(1)修改SearchServiceImple.java -> 开启分页查询
    //当前页
    String pageNum = searchMap.get("pageNum");
    //每页显示数
    String pageSize = searchMap.get("pageSize");
    //默认第1页
    if(StringUtils.isEmpty(pageNum)){
        pageNum="1";
    }
    //默认30页
    if(StringUtils.isEmpty(pageSize)){
        pageSize="30";  
    }
    
(2) //设置分页, 当前页从0开始, 每页显示条数
    nativeSearchQueryBuilder.withPageable(PageRequest.of(Integer.parseInt(pageNum)-1, Integer.parseInt(pageSize)));

(3)返回当前页
    //封装当前页
    resultMap.put("pageNum", pageNum)

六. 排序查询

1. 分类: 销量排序 / 新品排序 /评价排序

(1). 价格: 降序(高->低), 升序(低->高)

(2). 评价: 好中 差评 -> 数据库3个列 -> 记录评价数量 -> 排序时按照好评的比例排序 -> 条数限制(评价数>N条)

(3). 新品: 商品发布时间 / 更新时间

(4). 销量: 销售数量 -> 时间段限制
(1) 修改SearchServiceImpl.java
    //按照相关字段排序查询
    //1.当前域   2.升序ASC / 降序DESC  if(StringUtils.isNotEmpty(searchMap.get("sortField"))&&StringUtils.isNotEmpty("sortRule"))){
     //升序
     if("ASC".equals(searchMap.get("sortRule"))){
        //条件查询对象
        nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort(searchMap.get("sortField))).order(SortOrder.ASC));
    }else{
       nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort(searchMap.get("sortField))).order(SortOrder.DESC));
    }
}

七. 高亮查询

1. 关键词添加html标签(color red) -> 高亮显示搜索条件

2. 通过ES指定高亮域 -> 指定前后缀/html样式 -> 高亮搜索 -> 高亮数据替换非高亮

3.实现:

(1) 基于nativeSearchQueryBuilder对象 -> 设置高亮域 -> 高亮样式
    
    HighlightBuilder.Field field = new HighlightBuilder.Field("name") //高亮域
        .preTags("<span style='color:red'>")   //高亮前缀
        .postTags("</span>");                 //高亮后缀

    nativeSearchQueryBuilder.withHighlighFields(fields);

(2) 高亮数据替换非高亮数据
    Map<String, HighlightField> highlightFields = hit.getHighLightFields(); //得到高亮域
    if(highlightFields !=null&&highlightFields.size()>0){
        //替换数据
        skuInfo.setName(highlightFields.get("name").getFragmetns()[0].toString());
    }
    

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值