Loading

条件筛选

用户有可能会根据分类搜索、品牌搜索,还有可能根据规格搜索,以及价格搜索和排序操作。根据分类和品牌搜索的时候,可以直接根据指定域搜索,而规格搜索的域数据是不确定的,价格是一个区间搜索,所以我们可以分为三段实现,先实现分类、品牌搜素,再实现规格搜索,然后实现价格区间搜索。

品牌筛选

需求分析

页面每次向后台传入对应的分类和品牌,后台据分类和品牌进行条件过滤即可。

代码实现

修改搜索微服务com.changgou.service.SearchServiceImpl的搜索方法,添加品牌过滤,代码如下:

代码如下:

@Override
public Map search(Map<String, String> searchMap) throws Exception {
    Map<String, Object> resultMap = new HashMap<>();
​
    //有条件才查询Es
    if (null != searchMap) {
        //组合条件对象
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        //0:关键词
        if (StringUtils.isNotEmpty(searchMap.get("keywords"))) {
            boolQuery.must(QueryBuilders.matchQuery("name", searchMap.get("keywords")).operator(Operator.AND));
        }
        //1:条件 品牌
        if (StringUtils.isNotEmpty(searchMap.get("brand"))) {
            boolQuery.filter(QueryBuilders.termQuery("brandName", searchMap.get("brand")));
        }
        
        //4. 原生搜索实现类
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        nativeSearchQueryBuilder.withQuery(boolQuery);
​
        //6. 品牌聚合(分组)查询
        String skuBrand = "skuBrand";
nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms(skuBrand).field("brandName"));
​
        //10: 执行查询, 返回结果对象
        AggregatedPage<SkuInfo> aggregatedPage = esTemplate.queryForPage(nativeSearchQueryBuilder.build(), SkuInfo.class, new SearchResultMapper() {
            @Override
            public <T> AggregatedPage<T> mapResults(SearchResponse searchResponse, Class<T> aClass, Pageable pageable) {
​
                List<T> list = new ArrayList<>();
​
                SearchHits hits = searchResponse.getHits();
                if (null != hits) {
                    for (SearchHit hit : hits) {
                        SkuInfo skuInfo = JSON.parseObject(hit.getSourceAsString(), SkuInfo.class);
​
                        list.add((T) skuInfo);
                    }
                }
                return new AggregatedPageImpl<T>(list, pageable, hits.getTotalHits(), searchResponse.getAggregations());
            }
        });
​
        //11. 总条数
        resultMap.put("total", aggregatedPage.getTotalElements());
        //12. 总页数
        resultMap.put("totalPages", aggregatedPage.getTotalPages());
        //13. 查询结果集合
        resultMap.put("rows", aggregatedPage.getContent());
​
        //14. 获取品牌聚合结果
        StringTerms brandTerms = (StringTerms) aggregatedPage.getAggregation(skuBrand);
        List<String> brandList = brandTerms.getBuckets().stream().map(bucket -> bucket.getKeyAsString()).collect(Collectors.toList());
        resultMap.put("brandList", brandList);
​
        return resultMap;
    }
    return null;
}

测试

测试效果如下:

访问地址:http://localhost:9009/sku_search?keywords=手机

此时只能搜到华为手环设备

规格过滤

需求分析

规格这一部分,需要向后台发送规格名字以及规格值,我们可以按照一定要求来发送数据,例如规格名字以特殊前缀提交到后台:spec_网络制式:电信4G、spec_显示屏尺寸:4.0-4.9英寸

后台接到数据后,可以根据前缀spec_来区分是否是规格,如果以spec_xxx开始的数据则为规格数据,需要根据指定规格找信息。

上图是规格的索引存储格式,真实数据在spechMap.规格名字.keyword中,所以找数据也是按照如下格式去找:

spechMap.规格名字.keyword

代码实现

修改com.changgou.service.SearchServiceImpl的搜索方法,增加规格查询操作,代码如下:

代码如下:

@Override
public Map search(Map<String, String> searchMap) throws Exception {
    Map<String, Object> resultMap = new HashMap<>();
​
    //有条件才查询Es
    if (null != searchMap) {
        //组合条件对象
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        //0:关键词
        if (!StringUtils.isEmpty(searchMap.get("keywords"))) {
            boolQuery.must(QueryBuilders.matchQuery("name", searchMap.get("keywords")).operator(Operator.AND));
        }
        //1:条件 品牌
        if (!StringUtils.isEmpty(searchMap.get("brand"))) {
            boolQuery.filter(QueryBuilders.termQuery("brandName", searchMap.get("brand")));
        }
​
        //2:条件 规格
        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));
            }
        }
​
        //4. 原生搜索实现类
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        nativeSearchQueryBuilder.withQuery(boolQuery);
​
​
        //6. 品牌聚合(分组)查询
        String skuBrand = "skuBrand";
 nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms(skuBrand).field("brandName"));
​
        //7. 规格聚合(分组)查询
        String skuSpec = "skuSpec";
 nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms(skuSpec).field("spec.keyword"));
​
        //10: 执行查询, 返回结果对象
        AggregatedPage<SkuInfo> aggregatedPage = esTemplate.queryForPage(nativeSearchQueryBuilder.build(), SkuInfo.class, new SearchResultMapper() {
            @Override
            public <T> AggregatedPage<T> mapResults(SearchResponse searchResponse, Class<T> aClass, Pageable pageable) {
​
                List<T> list = new ArrayList<>();
​
                SearchHits hits = searchResponse.getHits();
                if (null != hits) {
                    for (SearchHit hit : hits) {
                        SkuInfo skuInfo = JSON.parseObject(hit.getSourceAsString(), SkuInfo.class);
​
                        list.add((T) skuInfo);
                    }
                }
                return new AggregatedPageImpl<T>(list, pageable, hits.getTotalHits(), searchResponse.getAggregations());
            }
        });
​
        //11. 总条数
        resultMap.put("total", aggregatedPage.getTotalElements());
        //12. 总页数
        resultMap.put("totalPages", aggregatedPage.getTotalPages());
        //13. 查询结果集合
        resultMap.put("rows", aggregatedPage.getContent());
​
        //14. 获取品牌聚合结果
        StringTerms brandTerms = (StringTerms) aggregatedPage.getAggregation(skuBrand);
        List<String> brandList = brandTerms.getBuckets().stream().map(bucket -> bucket.getKeyAsString()).collect(Collectors.toList());
        resultMap.put("brandList", brandList);
​
        //15. 获取规格聚合结果
        StringTerms specTerms = (StringTerms) aggregatedPage.getAggregation(skuSpec);
        List<String> specList = specTerms.getBuckets().stream().map(bucket -> bucket.getKeyAsString()).collect(Collectors.toList());
        resultMap.put("specList", specList(specList));
​
        return resultMap;
    }
    return null;
}
​
//处理规格集合
public Map<String, Set<String>> specList(List<String> specList) {
​
    Map<String, Set<String>> specMap = new HashMap<>();
​
    if (null != specList && specList.size() > 0) {
​
        for (String spec : specList) {
​
            Map<String, String> map = JSON.parseObject(spec, Map.class);
            Set<Map.Entry<String, String>> entries = map.entrySet();
            for (Map.Entry<String, String> entry : entries) {
                String key = entry.getKey();
                String value = entry.getValue();
​
                Set<String> specValues = specMap.get(key);
                if (null == specValues) {
                    specValues = new HashSet<>();
                }
                specValues.add(value);
                specMap.put(key, specValues);
            }
        }
    }
    return specMap;
}

测试

访问地址:http://localhost:9009/sku_search?keywords=手机

 

价格区间查询

需求分析

价格区间查询,每次需要将价格传入到后台,前端传入后台的价格大概是price=0-500或者price=500-1000依次类推,最后一个是price=3000,后台可以根据-分割,如果分割得到的结果最多有2个,第1个表示x<price,第2个表示price<=y

代码实现

修改com.changgou.service.impl.SearchServiceImpl的搜索方法,增加价格区间查询操作,代码如下:

代码如下:

@Override
public Map search(Map<String, String> searchMap) throws Exception {
    Map<String, Object> resultMap = new HashMap<>();
​
    //有条件才查询Es
    if (null != searchMap) {
        //组合条件对象
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        //0:关键词
        if (!StringUtils.isEmpty(searchMap.get("keywords"))) {
            boolQuery.must(QueryBuilders.matchQuery("name", searchMap.get("keywords")).operator(Operator.AND));
        }
        //1:条件 品牌
        if (!StringUtils.isEmpty(searchMap.get("brand"))) {
            boolQuery.filter(QueryBuilders.termQuery("brandName", searchMap.get("brand")));
        }
​
        //2:条件 规格
        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));
            }
        }
        //3:条件 价格
        if (StringUtils.isNotEmpty(searchMap.get("price"))) {
            String[] p = searchMap.get("price").split("-");
           if (p.length == 2) {
                boolQuery.filter(QueryBuilders.rangeQuery("price").lte(p[1]));
            }  boolQuery.filter(QueryBuilders.rangeQuery("price").gte(p[0]));
        }
​
        //4. 原生搜索实现类
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        nativeSearchQueryBuilder.withQuery(boolQuery);
​
        //6. 品牌聚合(分组)查询
        String skuBrand = "skuBrand";
        nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms(skuBrand).field("brandName"));
​
        //7. 规格聚合(分组)查询
        String skuSpec = "skuSpec";
        nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms(skuSpec).field("spec.keyword"));
​
        //10: 执行查询, 返回结果对象
        AggregatedPage<SkuInfo> aggregatedPage = esTemplate.queryForPage(nativeSearchQueryBuilder.build(), SkuInfo.class, new SearchResultMapper() {
            @Override
            public <T> AggregatedPage<T> mapResults(SearchResponse searchResponse, Class<T> aClass, Pageable pageable) {
​
                List<T> list = new ArrayList<>();
​
                SearchHits hits = searchResponse.getHits();
                if (null != hits) {
                    for (SearchHit hit : hits) {
                        SkuInfo skuInfo = JSON.parseObject(hit.getSourceAsString(), SkuInfo.class);
​
                        list.add((T) skuInfo);
                    }
                }
                return new AggregatedPageImpl<T>(list, pageable, hits.getTotalHits(), searchResponse.getAggregations());
            }
        });
​
        //11. 总条数
        resultMap.put("total", aggregatedPage.getTotalElements());
        //12. 总页数
        resultMap.put("totalPages", aggregatedPage.getTotalPages());
        //13. 查询结果集合
        resultMap.put("rows", aggregatedPage.getContent());
​
        //14. 获取品牌聚合结果
        StringTerms brandTerms = (StringTerms) aggregatedPage.getAggregation(skuBrand);
        List<String> brandList = brandTerms.getBuckets().stream().map(bucket -> bucket.getKeyAsString()).collect(Collectors.toList());
        resultMap.put("brandList", brandList);
​
        //15. 获取规格聚合结果
        StringTerms specTerms = (StringTerms) aggregatedPage.getAggregation(skuSpec);
        List<String> specList = specTerms.getBuckets().stream().map(bucket -> bucket.getKeyAsString()).collect(Collectors.toList());
        resultMap.put("specList", specList(specList));
​
        return resultMap;
    }
​
    return null;
}

测试

访问地址:http://localhost:9009/sku_search?keywords=手机

效果如下(部分数据):

[
        {
            "id": 1088256019328536576,
            "name": "守护宝幼儿安全手环",
            "price": 500,
            "num": 100,
            "image": "http://img10.360buyimg.com/n1/s450x450_jfs/t3457/294/236823024/102048/c97f5825/58072422Ndd7e66c4.jpg",
            "status": "1",
            "createTime": "2019-01-24T10:03:48.000+0000",
            "updateTime": "2019-01-24T10:03:48.000+0000",
            "isDefault": null,
            "spuId": 1088256019315953664,
            "categoryId": 1108,
            "categoryName": "户外工具",
            "brandName": "守护宝",
            "spec": "{\"颜色\":\"红\",\"机身内存\":\"64G\"}",
            "specMap": {
                "颜色": "红",
                "机身内存": "64G"
            }
        },
        {
            "id": 1088256014043713536,
            "name": "计步器小米手环,适用老人、小孩",
            "price": 800,
            "num": 100,
            "image": "http://img10.360buyimg.com/n1/s450x450_jfs/t3457/294/236823024/102048/c97f5825/58072422Ndd7e66c4.jpg",
            "status": "1",
            "createTime": "2019-01-24T10:03:47.000+0000",
            "updateTime": "2019-01-24T10:03:47.000+0000",
            "isDefault": null,
            "spuId": 1088256014026936320,
            "categoryId": 1192,
            "categoryName": "小家电",
            "brandName": "小米",
            "spec": "{\"颜色\":\"红\",\"机身内存\":\"64G\"}",
            "specMap": {
                "颜色": "红",
                "机身内存": "64G"
            }
        }
    ]

 

posted @ 2021-08-04 09:37  1640808365  阅读(195)  评论(0编辑  收藏  举报