• 2022谷粒商城学习笔记(十六)检索服务


    前言

    本系列博客基于B站谷粒商城,只作为本人学习总结使用。这里我会比较注重业务逻辑的编写和相关配置的流程。有问题可以评论或者联系我互相交流。原视频地址谷粒商城雷丰阳版。本人git仓库地址Draknessssw的谷粒商城


    页面查询数据

    在这里插入图片描述
    根据传递来的页面的查询参数,去es中检索商品

    package com.xxxx.gulimall.search.controller;
    
    import com.xxxx.gulimall.search.service.MallSearchService;
    import com.xxxx.gulimall.search.vo.SearchParam;
    import com.xxxx.gulimall.search.vo.SearchResult;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.GetMapping;
    
    import javax.servlet.http.HttpServletRequest;
    
    
    /**
     * @author LinLinD
     * @Create 2022-07-16-20:49
     */
    @Controller
    public class SearchController {
    
        @Autowired
        private MallSearchService mallSearchService;
    
        /**
         * 自动将页面提交过来的所有请求参数封装成我们指定的对象
         * @param param
         * @return
         */
    
    
        @GetMapping(value = "/list.html")
        public String listPage(SearchParam param, Model model, HttpServletRequest request) {
    
            param.set_queryString(request.getQueryString());
    
            //1、根据传递来的页面的查询参数,去es中检索商品
            SearchResult result = mallSearchService.search(param);
    
            model.addAttribute("result",result);
    
            return "list";
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    封装查询条件vo

    package com.xxxx.gulimall.search.vo;
    
    import lombok.Data;
    
    import java.util.List;
    
    
    
    @Data
    public class SearchParam {
    
        /**
         * 页面传递过来的全文匹配关键字
         */
        private String keyword;
    
        /**
         * 品牌id,可以多选
         */
        private List<Long> brandId;
    
        /**
         * 三级分类id
         */
        private Long catalog3Id;
    
        /**
         * 排序条件:sort=price/salecount/hotscore_desc/asc
         */
        private String sort;
    
        /**
         * 是否显示有货
         */
        private Integer hasStock;
    
        /**
         * 价格区间查询
         */
        private String skuPrice;
    
        /**
         * 按照属性进行筛选
         */
        private List<String> attrs;
    
        /**
         * 页码
         */
        private Integer pageNum = 1;
    
        /**
         * 原生的所有查询条件
         */
        private String _queryString;
    
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59

    返回结果数据vo

    package com.xxxx.gulimall.search.vo;
    
    
    import com.xxxx.common.to.es.SkuEsModel;
    import lombok.Data;
    
    import java.util.ArrayList;
    import java.util.List;
    
    
    @Data
    public class SearchResult {
    
        /**
         * 查询到的所有商品信息
         */
        private List<SkuEsModel> product;
    
    
        /**
         * 当前页码
         */
        private Integer pageNum;
    
        /**
         * 总记录数
         */
        private Long total;
    
        /**
         * 总页码
         */
        private Integer totalPages;
    
        private List<Integer> pageNavs;
    
        /**
         * 当前查询到的结果,所有涉及到的品牌
         */
        private List<BrandVo> brands;
    
        /**
         * 当前查询到的结果,所有涉及到的所有属性
         */
        private List<AttrVo> attrs;
    
        /**
         * 当前查询到的结果,所有涉及到的所有分类
         */
        private List<CatalogVo> catalogs;
    
    
        //===========================以上是返回给页面的所有信息============================//
    
    
        /** 导航数据*/
        private List<NavVo> navs = new ArrayList<>();
    
        /** 便于判断当前id是否被使用*/
        private List<Long> attrIds = new ArrayList<>();
    
        @Data
        public static class NavVo {
            private String navName;
            private String navValue;
            private String link;
        }
    
    
        @Data
        public static class BrandVo {
    
            private Long brandId;
    
            private String brandName;
    
            private String brandImg;
        }
    
    
        @Data
        public static class AttrVo {
    
            private Long attrId;
    
            private String attrName;
    
            private List<String> attrValue;
        }
    
    
        @Data
        public static class CatalogVo {
    
            private Long catalogId;
    
            private String catalogName;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99

    实现类

    	@Autowired
        private RestHighLevelClient esRestClient;
    
    • 1
    • 2
    	@Override
        public SearchResult search(SearchParam param) {
    
            //1、动态构建出查询需要的DSL语句
            SearchResult result = null;
    
            //1、准备检索请求
            SearchRequest searchRequest = buildSearchRequest(param);
    
            try {
                //2、执行检索请求
                SearchResponse response = esRestClient.search(searchRequest, GulimallElasticSearchConfig.COMMON_OPTIONS);
    
                //3、分析响应数据,封装成我们需要的格式
                result = buildSearchResult(response,param);
            } catch (IOException e) {
                e.printStackTrace();
            }
    
            return result;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    构建检索请求方法
    新建SearchSourceBuilder 用于构建dsl语句

    SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    
    • 1

    较为特殊的是商品的属性,因为商品属性值的储存以attrs=1_5寸:8寸&2_16G:8G这种形式储存,所以首先利用split方法分割出属性id和属性值,然后是属性值的分割。商品价格查询条件同理。其他的用boolQueryBuilder封装即可

    		/**
             * 模糊匹配,过滤(按照属性,分类,品牌,价格区间,库存)
             */
            //1. 构建bool-query
            BoolQueryBuilder boolQueryBuilder=new BoolQueryBuilder();
    
            //1.1 bool-must
            if(!StringUtils.isEmpty(param.getKeyword())){
                boolQueryBuilder.must(QueryBuilders.matchQuery("skuTitle",param.getKeyword()));
            }
    
            //1.2 bool-fiter
            //1.2.1 catelogId
            if(null != param.getCatalog3Id()){
                boolQueryBuilder.filter(QueryBuilders.termQuery("catalogId",param.getCatalog3Id()));
            }
    
            //1.2.2 brandId
            if(null != param.getBrandId() && param.getBrandId().size() >0){
                boolQueryBuilder.filter(QueryBuilders.termsQuery("brandId",param.getBrandId()));
            }
    
            //1.2.3 attrs
            if(param.getAttrs() != null && param.getAttrs().size() > 0){
    
                param.getAttrs().forEach(item -> {
                    //attrs=1_5寸:8寸&2_16G:8G
                    BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
    
    
                    //attrs=1_5寸:8寸
                    String[] s = item.split("_");
                    String attrId=s[0];
                    String[] attrValues = s[1].split(":");//这个属性检索用的值
                    boolQuery.must(QueryBuilders.termQuery("attrs.attrId",attrId));
                    boolQuery.must(QueryBuilders.termsQuery("attrs.attrValue",attrValues));
    
                    NestedQueryBuilder nestedQueryBuilder = QueryBuilders.nestedQuery("attrs",boolQuery, ScoreMode.None);
                    boolQueryBuilder.filter(nestedQueryBuilder);
                });
    
            }
    
            //1.2.4 hasStock
            if(null != param.getHasStock()){
                boolQueryBuilder.filter(QueryBuilders.termQuery("hasStock",param.getHasStock() == 1));
            }
    
    		//1.2.5 skuPrice
            if(!StringUtils.isEmpty(param.getSkuPrice())){
                //skuPrice形式为:1_500或_500或500_
                RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("skuPrice");
                String[] price = param.getSkuPrice().split("_");
                if(price.length==2){
                    rangeQueryBuilder.gte(price[0]).lte(price[1]);
                }else if(price.length == 1){
                    if(param.getSkuPrice().startsWith("_")){
                        rangeQueryBuilder.lte(price[1]);
                    }
                    if(param.getSkuPrice().endsWith("_")){
                        rangeQueryBuilder.gte(price[0]);
                    }
                }
                boolQueryBuilder.filter(rangeQueryBuilder);
            }
    
            //封装所有的查询条件
            searchSourceBuilder.query(boolQueryBuilder);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68

    排序、分页、高亮查询

    排序的字段储存形式为sort=hotScore_asc/desc

    		if(!StringUtils.isEmpty(param.getSort())){
                String sort = param.getSort();
                String[] sortFileds = sort.split("_");
    
                SortOrder sortOrder="asc".equalsIgnoreCase(sortFileds[1])?SortOrder.ASC:SortOrder.DESC;
    
                searchSourceBuilder.sort(sortFileds[0],sortOrder);
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    分页的数据从0开始查页码,数据行数自己指定数目。

    searchSourceBuilder.from((param.getPageNum()-1)*EsConstant.PRODUCT_PAGESIZE);
            searchSourceBuilder.size(EsConstant.PRODUCT_PAGESIZE);
    
    • 1
    • 2

    对于商品的关键字,以html的红色效果展示

    		//高亮
            if(!StringUtils.isEmpty(param.getKeyword())){
    
                HighlightBuilder highlightBuilder = new HighlightBuilder();
                highlightBuilder.field("skuTitle");
                highlightBuilder.preTags("");
                highlightBuilder.postTags("");
    
                searchSourceBuilder.highlighter(highlightBuilder);
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    聚合dsl构建。最后封装index索引返回。

    		/**
             * 聚合分析
             */
            //1. 按照品牌进行聚合
            TermsAggregationBuilder brand_agg = AggregationBuilders.terms("brand_agg");
            brand_agg.field("brandId").size(50);
    
    
            //1.1 品牌的子聚合-品牌名聚合
            brand_agg.subAggregation(AggregationBuilders.terms("brand_name_agg")
                    .field("brandName").size(1));
            //1.2 品牌的子聚合-品牌图片聚合
            brand_agg.subAggregation(AggregationBuilders.terms("brand_img_agg")
                    .field("brandImg").size(1));
    
            searchSourceBuilder.aggregation(brand_agg);
    
            //2. 按照分类信息进行聚合
            TermsAggregationBuilder catalog_agg = AggregationBuilders.terms("catalog_agg");
            catalog_agg.field("catalogId").size(20);
    
            catalog_agg.subAggregation(AggregationBuilders.terms("catalog_name_agg").field("catalogName").size(1));
    
            searchSourceBuilder.aggregation(catalog_agg);
    
            //2. 按照属性信息进行聚合
            NestedAggregationBuilder attr_agg = AggregationBuilders.nested("attr_agg", "attrs");
            //2.1 按照属性ID进行聚合
            TermsAggregationBuilder attr_id_agg = AggregationBuilders.terms("attr_id_agg").field("attrs.attrId");
            attr_agg.subAggregation(attr_id_agg);
            //2.1.1 在每个属性ID下,按照属性名进行聚合
            attr_id_agg.subAggregation(AggregationBuilders.terms("attr_name_agg").field("attrs.attrName").size(1));
            //2.1.1 在每个属性ID下,按照属性值进行聚合
            attr_id_agg.subAggregation(AggregationBuilders.terms("attr_value_agg").field("attrs.attrValue").size(50));
            searchSourceBuilder.aggregation(attr_agg);
    
            log.debug("构建的DSL语句 {}",searchSourceBuilder.toString());
    
            SearchRequest searchRequest = new SearchRequest(new String[]{EsConstant.PRODUCT_INDEX},searchSourceBuilder);
    
            return searchRequest;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    最终效果

    	/**
         * 准备检索请求
         * 模糊匹配,过滤(按照属性,分类,品牌,价格区间,库存),排序,分页,高亮,聚合分析
         * @return
         */
        private SearchRequest buildSearchRequest(SearchParam param) {
    
            SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    
            /**
             * 模糊匹配,过滤(按照属性,分类,品牌,价格区间,库存)
             */
            //1. 构建bool-query
            BoolQueryBuilder boolQueryBuilder=new BoolQueryBuilder();
    
            //1.1 bool-must
            if(!StringUtils.isEmpty(param.getKeyword())){
                boolQueryBuilder.must(QueryBuilders.matchQuery("skuTitle",param.getKeyword()));
            }
    
            //1.2 bool-fiter
            //1.2.1 catelogId
            if(null != param.getCatalog3Id()){
                boolQueryBuilder.filter(QueryBuilders.termQuery("catalogId",param.getCatalog3Id()));
            }
    
            //1.2.2 brandId
            if(null != param.getBrandId() && param.getBrandId().size() >0){
                boolQueryBuilder.filter(QueryBuilders.termsQuery("brandId",param.getBrandId()));
            }
    
            //1.2.3 attrs
            if(param.getAttrs() != null && param.getAttrs().size() > 0){
    
                param.getAttrs().forEach(item -> {
                    //attrs=1_5寸:8寸&2_16G:8G
                    BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
    
    
                    //attrs=1_5寸:8寸
                    String[] s = item.split("_");
                    String attrId=s[0];
                    String[] attrValues = s[1].split(":");//这个属性检索用的值
                    boolQuery.must(QueryBuilders.termQuery("attrs.attrId",attrId));
                    boolQuery.must(QueryBuilders.termsQuery("attrs.attrValue",attrValues));
    
                    NestedQueryBuilder nestedQueryBuilder = QueryBuilders.nestedQuery("attrs",boolQuery, ScoreMode.None);
                    boolQueryBuilder.filter(nestedQueryBuilder);
                });
    
            }
    
            //1.2.4 hasStock
            if(null != param.getHasStock()){
                boolQueryBuilder.filter(QueryBuilders.termQuery("hasStock",param.getHasStock() == 1));
            }
    
    
            //1.2.5 skuPrice
            if(!StringUtils.isEmpty(param.getSkuPrice())){
                //skuPrice形式为:1_500或_500或500_
                RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("skuPrice");
                String[] price = param.getSkuPrice().split("_");
                if(price.length==2){
                    rangeQueryBuilder.gte(price[0]).lte(price[1]);
                }else if(price.length == 1){
                    if(param.getSkuPrice().startsWith("_")){
                        rangeQueryBuilder.lte(price[1]);
                    }
                    if(param.getSkuPrice().endsWith("_")){
                        rangeQueryBuilder.gte(price[0]);
                    }
                }
                boolQueryBuilder.filter(rangeQueryBuilder);
            }
    
            //封装所有的查询条件
            searchSourceBuilder.query(boolQueryBuilder);
    
    
            /**
             * 排序,分页,高亮
             */
    
            //排序
            //形式为sort=hotScore_asc/desc
            if(!StringUtils.isEmpty(param.getSort())){
                String sort = param.getSort();
                String[] sortFileds = sort.split("_");
    
                SortOrder sortOrder="asc".equalsIgnoreCase(sortFileds[1])?SortOrder.ASC:SortOrder.DESC;
    
                searchSourceBuilder.sort(sortFileds[0],sortOrder);
            }
    
            //分页
            searchSourceBuilder.from((param.getPageNum()-1)*EsConstant.PRODUCT_PAGESIZE);
            searchSourceBuilder.size(EsConstant.PRODUCT_PAGESIZE);
    
            //高亮
            if(!StringUtils.isEmpty(param.getKeyword())){
    
                HighlightBuilder highlightBuilder = new HighlightBuilder();
                highlightBuilder.field("skuTitle");
                highlightBuilder.preTags("");
                highlightBuilder.postTags("");
    
                searchSourceBuilder.highlighter(highlightBuilder);
            }
    
    
    
            /**
             * 聚合分析
             */
            //1. 按照品牌进行聚合
            TermsAggregationBuilder brand_agg = AggregationBuilders.terms("brand_agg");
            brand_agg.field("brandId").size(50);
    
    
            //1.1 品牌的子聚合-品牌名聚合
            brand_agg.subAggregation(AggregationBuilders.terms("brand_name_agg")
                    .field("brandName").size(1));
            //1.2 品牌的子聚合-品牌图片聚合
            brand_agg.subAggregation(AggregationBuilders.terms("brand_img_agg")
                    .field("brandImg").size(1));
    
            searchSourceBuilder.aggregation(brand_agg);
    
            //2. 按照分类信息进行聚合
            TermsAggregationBuilder catalog_agg = AggregationBuilders.terms("catalog_agg");
            catalog_agg.field("catalogId").size(20);
    
            catalog_agg.subAggregation(AggregationBuilders.terms("catalog_name_agg").field("catalogName").size(1));
    
            searchSourceBuilder.aggregation(catalog_agg);
    
            //2. 按照属性信息进行聚合
            NestedAggregationBuilder attr_agg = AggregationBuilders.nested("attr_agg", "attrs");
            //2.1 按照属性ID进行聚合
            TermsAggregationBuilder attr_id_agg = AggregationBuilders.terms("attr_id_agg").field("attrs.attrId");
            attr_agg.subAggregation(attr_id_agg);
            //2.1.1 在每个属性ID下,按照属性名进行聚合
            attr_id_agg.subAggregation(AggregationBuilders.terms("attr_name_agg").field("attrs.attrName").size(1));
            //2.1.1 在每个属性ID下,按照属性值进行聚合
            attr_id_agg.subAggregation(AggregationBuilders.terms("attr_value_agg").field("attrs.attrValue").size(50));
            searchSourceBuilder.aggregation(attr_agg);
    
            log.debug("构建的DSL语句 {}",searchSourceBuilder.toString());
    
            SearchRequest searchRequest = new SearchRequest(new String[]{EsConstant.PRODUCT_INDEX},searchSourceBuilder);
    
            return searchRequest;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154

    在elasticsearch里面检索之后的结果构建成需要的数据格式
    获取elasticsearch的商品信息,先对关键字进行判断是否高亮。

    //1、返回的所有查询到的商品
            SearchHits hits = response.getHits();
    
            List<SkuEsModel> esModels = new ArrayList<>();
            //遍历所有商品信息
            if (hits.getHits() != null && hits.getHits().length > 0) {
                for (SearchHit hit : hits.getHits()) {
                    String sourceAsString = hit.getSourceAsString();
                    SkuEsModel esModel = JSON.parseObject(sourceAsString, SkuEsModel.class);
    
                    //判断是否按关键字检索,若是就显示高亮,否则不显示
                    if (!StringUtils.isEmpty(param.getKeyword())) {
                        //拿到高亮信息显示标题
                        HighlightField skuTitle = hit.getHighlightFields().get("skuTitle");
                        String skuTitleValue = skuTitle.getFragments()[0].string();
                        esModel.setSkuTitle(skuTitleValue);
                    }
                    esModels.add(esModel);
                }
            }
            result.setProduct(esModels);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    获取属性、品牌、三级分类的聚合信息封装进结果Vo

    //2、当前商品涉及到的所有属性信息
            List<SearchResult.AttrVo> attrVos = new ArrayList<>();
            //获取属性信息的聚合
            ParsedNested attrsAgg = response.getAggregations().get("attr_agg");
            ParsedLongTerms attrIdAgg = attrsAgg.getAggregations().get("attr_id_agg");
            for (Terms.Bucket bucket : attrIdAgg.getBuckets()) {
                SearchResult.AttrVo attrVo = new SearchResult.AttrVo();
                //1、得到属性的id
                long attrId = bucket.getKeyAsNumber().longValue();
                attrVo.setAttrId(attrId);
    
                //2、得到属性的名字
                ParsedStringTerms attrNameAgg = bucket.getAggregations().get("attr_name_agg");
                String attrName = attrNameAgg.getBuckets().get(0).getKeyAsString();
                attrVo.setAttrName(attrName);
    
                //3、得到属性的所有值
                ParsedStringTerms attrValueAgg = bucket.getAggregations().get("attr_value_agg");
                List<String> attrValues = attrValueAgg.getBuckets().stream().map(item -> item.getKeyAsString()).collect(Collectors.toList());
                attrVo.setAttrValue(attrValues);
    
                attrVos.add(attrVo);
            }
    
            result.setAttrs(attrVos);
    
            //3、当前商品涉及到的所有品牌信息
            List<SearchResult.BrandVo> brandVos = new ArrayList<>();
            //获取到品牌的聚合
            ParsedLongTerms brandAgg = response.getAggregations().get("brand_agg");
            for (Terms.Bucket bucket : brandAgg.getBuckets()) {
                SearchResult.BrandVo brandVo = new SearchResult.BrandVo();
    
                //1、得到品牌的id
                long brandId = bucket.getKeyAsNumber().longValue();
                brandVo.setBrandId(brandId);
    
                //2、得到品牌的名字
                ParsedStringTerms brandNameAgg = bucket.getAggregations().get("brand_name_agg");
                String brandName = brandNameAgg.getBuckets().get(0).getKeyAsString();
                brandVo.setBrandName(brandName);
    
                //3、得到品牌的图片
                ParsedStringTerms brandImgAgg = bucket.getAggregations().get("brand_img_agg");
                String brandImg = brandImgAgg.getBuckets().get(0).getKeyAsString();
                brandVo.setBrandImg(brandImg);
    
                brandVos.add(brandVo);
            }
            result.setBrands(brandVos);
    
            //4、当前商品涉及到的所有分类信息
            //获取到分类的聚合
            List<SearchResult.CatalogVo> catalogVos = new ArrayList<>();
            ParsedLongTerms catalogAgg = response.getAggregations().get("catalog_agg");
            for (Terms.Bucket bucket : catalogAgg.getBuckets()) {
                SearchResult.CatalogVo catalogVo = new SearchResult.CatalogVo();
                //得到分类id
                String keyAsString = bucket.getKeyAsString();
                catalogVo.setCatalogId(Long.parseLong(keyAsString));
    
                //得到分类名
                ParsedStringTerms catalogNameAgg = bucket.getAggregations().get("catalog_name_agg");
                String catalogName = catalogNameAgg.getBuckets().get(0).getKeyAsString();
                catalogVo.setCatalogName(catalogName);
                catalogVos.add(catalogVo);
            }
    
            result.setCatalogs(catalogVos);
            //===============以上可以从聚合信息中获取====================//
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70

    分页要是不满足自定义的每一页的数据行数,那么页数加一。

    //5、2分页信息-总页码-计算
            int totalPages = (int)total % EsConstant.PRODUCT_PAGESIZE == 0 ?
                    (int)total / EsConstant.PRODUCT_PAGESIZE : ((int)total / EsConstant.PRODUCT_PAGESIZE + 1);
            result.setTotalPages(totalPages);
    
            List<Integer> pageNavs = new ArrayList<>();
            for (int i = 1; i <= totalPages; i++) {
                pageNavs.add(i);
            }
            result.setPageNavs(pageNavs);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    面包屑导航
    需要远程查询商品的属性信息

    远程调用接口

    package com.xxxx.gulimall.search.feign;
    
    import com.xxxx.common.utils.R;
    import org.springframework.cloud.openfeign.FeignClient;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    
    
    @FeignClient("gulimall-product")
    public interface ProductFeignService {
    
        @GetMapping("/product/attr/info/{attrId}")
        public R attrInfo(@PathVariable("attrId") Long attrId);
    
    //    @GetMapping("product/brand/infos")
    //    public R BrandsInfo(@RequestParam("brandIds") List brandIds);
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    要是远程调用成功,使用远程调用查到的名字导航,否则用当前查询的商品属性id导航。

    				//1、分析每一个attrs传过来的参数值
                    SearchResult.NavVo navVo = new SearchResult.NavVo();
                    String[] s = attr.split("_");
                    navVo.setNavValue(s[1]);
                    R r = productFeignService.attrInfo(Long.parseLong(s[0]));
                    if (r.getCode() == 0) {
                        AttrResponseVo data = r.getData("attr", new TypeReference<AttrResponseVo>() {
                        });
                        navVo.setNavName(data.getAttrName());
                    } else {
                        navVo.setNavName(s[0]);
                    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    要是取消了面包屑导航,得从整个请求url删除属性的。注意将url转为utf-8编码以后,将代表空格的+替换为url的%20
    在这里插入图片描述

    //6、构建面包屑导航
            if (param.getAttrs() != null && param.getAttrs().size() > 0) {
                List<SearchResult.NavVo> collect = param.getAttrs().stream().map(attr -> {
                    //1、分析每一个attrs传过来的参数值
                    SearchResult.NavVo navVo = new SearchResult.NavVo();
                    String[] s = attr.split("_");
                    navVo.setNavValue(s[1]);
                    R r = productFeignService.attrInfo(Long.parseLong(s[0]));
                    if (r.getCode() == 0) {
                        AttrResponseVo data = r.getData("attr", new TypeReference<AttrResponseVo>() {
                        });
                        navVo.setNavName(data.getAttrName());
                    } else {
                        navVo.setNavName(s[0]);
                    }
    
                    //2、取消了这个面包屑以后,我们要跳转到哪个地方,将请求的地址url里面的当前置空
                    //拿到所有的查询条件,去掉当前
                    String encode = null;
                    try {
                        encode = URLEncoder.encode(attr,"UTF-8");
                        encode.replace("+","%20");  //浏览器对空格的编码和Java不一样,差异化处理
                    } catch (UnsupportedEncodingException e) {
                        e.printStackTrace();
                    }
                    String replace = param.get_queryString().replace("&attrs=" + attr, "");
                    navVo.setLink("http://search.gulimall.com/list.html?" + replace);
    
                    return navVo;
                }).collect(Collectors.toList());
    
                result.setNavs(collect);
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    最终效果如下

    /**
         * 构建结果数据
         * 模糊匹配,过滤(按照属性、分类、品牌,价格区间,库存),完成排序、分页、高亮,聚合分析功能
         * @param response
         * @return
         */
        private SearchResult buildSearchResult(SearchResponse response,SearchParam param) {
    
            SearchResult result = new SearchResult();
    
            //1、返回的所有查询到的商品
            SearchHits hits = response.getHits();
    
            List<SkuEsModel> esModels = new ArrayList<>();
            //遍历所有商品信息
            if (hits.getHits() != null && hits.getHits().length > 0) {
                for (SearchHit hit : hits.getHits()) {
                    String sourceAsString = hit.getSourceAsString();
                    SkuEsModel esModel = JSON.parseObject(sourceAsString, SkuEsModel.class);
    
                    //判断是否按关键字检索,若是就显示高亮,否则不显示
                    if (!StringUtils.isEmpty(param.getKeyword())) {
                        //拿到高亮信息显示标题
                        HighlightField skuTitle = hit.getHighlightFields().get("skuTitle");
                        String skuTitleValue = skuTitle.getFragments()[0].string();
                        esModel.setSkuTitle(skuTitleValue);
                    }
                    esModels.add(esModel);
                }
            }
            result.setProduct(esModels);
    
            //2、当前商品涉及到的所有属性信息
            List<SearchResult.AttrVo> attrVos = new ArrayList<>();
            //获取属性信息的聚合
            ParsedNested attrsAgg = response.getAggregations().get("attr_agg");
            ParsedLongTerms attrIdAgg = attrsAgg.getAggregations().get("attr_id_agg");
            for (Terms.Bucket bucket : attrIdAgg.getBuckets()) {
                SearchResult.AttrVo attrVo = new SearchResult.AttrVo();
                //1、得到属性的id
                long attrId = bucket.getKeyAsNumber().longValue();
                attrVo.setAttrId(attrId);
    
                //2、得到属性的名字
                ParsedStringTerms attrNameAgg = bucket.getAggregations().get("attr_name_agg");
                String attrName = attrNameAgg.getBuckets().get(0).getKeyAsString();
                attrVo.setAttrName(attrName);
    
                //3、得到属性的所有值
                ParsedStringTerms attrValueAgg = bucket.getAggregations().get("attr_value_agg");
                List<String> attrValues = attrValueAgg.getBuckets().stream().map(item -> item.getKeyAsString()).collect(Collectors.toList());
                attrVo.setAttrValue(attrValues);
    
                attrVos.add(attrVo);
            }
    
            result.setAttrs(attrVos);
    
            //3、当前商品涉及到的所有品牌信息
            List<SearchResult.BrandVo> brandVos = new ArrayList<>();
            //获取到品牌的聚合
            ParsedLongTerms brandAgg = response.getAggregations().get("brand_agg");
            for (Terms.Bucket bucket : brandAgg.getBuckets()) {
                SearchResult.BrandVo brandVo = new SearchResult.BrandVo();
    
                //1、得到品牌的id
                long brandId = bucket.getKeyAsNumber().longValue();
                brandVo.setBrandId(brandId);
    
                //2、得到品牌的名字
                ParsedStringTerms brandNameAgg = bucket.getAggregations().get("brand_name_agg");
                String brandName = brandNameAgg.getBuckets().get(0).getKeyAsString();
                brandVo.setBrandName(brandName);
    
                //3、得到品牌的图片
                ParsedStringTerms brandImgAgg = bucket.getAggregations().get("brand_img_agg");
                String brandImg = brandImgAgg.getBuckets().get(0).getKeyAsString();
                brandVo.setBrandImg(brandImg);
    
                brandVos.add(brandVo);
            }
            result.setBrands(brandVos);
    
            //4、当前商品涉及到的所有分类信息
            //获取到分类的聚合
            List<SearchResult.CatalogVo> catalogVos = new ArrayList<>();
            ParsedLongTerms catalogAgg = response.getAggregations().get("catalog_agg");
            for (Terms.Bucket bucket : catalogAgg.getBuckets()) {
                SearchResult.CatalogVo catalogVo = new SearchResult.CatalogVo();
                //得到分类id
                String keyAsString = bucket.getKeyAsString();
                catalogVo.setCatalogId(Long.parseLong(keyAsString));
    
                //得到分类名
                ParsedStringTerms catalogNameAgg = bucket.getAggregations().get("catalog_name_agg");
                String catalogName = catalogNameAgg.getBuckets().get(0).getKeyAsString();
                catalogVo.setCatalogName(catalogName);
                catalogVos.add(catalogVo);
            }
    
            result.setCatalogs(catalogVos);
            //===============以上可以从聚合信息中获取====================//
            //5、分页信息-页码
            result.setPageNum(param.getPageNum());
            //5、1分页信息、总记录数
            long total = hits.getTotalHits().value;
            result.setTotal(total);
    
            //5、2分页信息-总页码-计算
            int totalPages = (int)total % EsConstant.PRODUCT_PAGESIZE == 0 ?
                    (int)total / EsConstant.PRODUCT_PAGESIZE : ((int)total / EsConstant.PRODUCT_PAGESIZE + 1);
            result.setTotalPages(totalPages);
    
            List<Integer> pageNavs = new ArrayList<>();
            for (int i = 1; i <= totalPages; i++) {
                pageNavs.add(i);
            }
            result.setPageNavs(pageNavs);
    
    
            //6、构建面包屑导航
            if (param.getAttrs() != null && param.getAttrs().size() > 0) {
                List<SearchResult.NavVo> collect = param.getAttrs().stream().map(attr -> {
                    //1、分析每一个attrs传过来的参数值
                    SearchResult.NavVo navVo = new SearchResult.NavVo();
                    String[] s = attr.split("_");
                    navVo.setNavValue(s[1]);
                    R r = productFeignService.attrInfo(Long.parseLong(s[0]));
                    if (r.getCode() == 0) {
                        AttrResponseVo data = r.getData("attr", new TypeReference<AttrResponseVo>() {
                        });
                        navVo.setNavName(data.getAttrName());
                    } else {
                        navVo.setNavName(s[0]);
                    }
    
                    //2、取消了这个面包屑以后,我们要跳转到哪个地方,将请求的地址url里面的当前置空
                    //拿到所有的查询条件,去掉当前
                    String encode = null;
                    try {
                        encode = URLEncoder.encode(attr,"UTF-8");
                        encode.replace("+","%20");  //浏览器对空格的编码和Java不一样,差异化处理
                    } catch (UnsupportedEncodingException e) {
                        e.printStackTrace();
                    }
                    String replace = param.get_queryString().replace("&attrs=" + attr, "");
                    navVo.setLink("http://search.gulimall.com/list.html?" + replace);
    
                    return navVo;
                }).collect(Collectors.toList());
    
                result.setNavs(collect);
            }
    
    
            return result;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
  • 相关阅读:
    ThinkPHP5目录结构
    丁鹿学堂前端培训:前端性能优化css篇(一)
    MyBatis-plus 代码生成器配置
    六、react组件通信-父子组件通信-子父组件通信-跨级组件的传参方式-context方式的传参
    SwiftUI4.0在iOS 16中新添加的inner和drop阴影效果
    Java多并发(四)| 锁(Lock接口 & AQS & ReentrantLock)
    【Linux】安装配置虚拟机及虚拟机操作系统的安装
    ReentrantLock锁与AQS的联系
    Node.js | 基础完结、综合训练 —— 路由应用实战教程
    Maven多模块管理(转载)
  • 原文地址:https://blog.csdn.net/qq_44737138/article/details/126630575