前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >商城项目-生成规格参数过滤

商城项目-生成规格参数过滤

作者头像
cwl_java
发布2020-02-11 13:56:49
7890
发布2020-02-11 13:56:49
举报
文章被收录于专栏:cwl_Javacwl_Java

3.生成规格参数过滤

3.1.谋而后动

有四个问题需要先思考清楚:

  • 什么时候显示规格参数过滤?
  • 如何知道哪些规格需要过滤?
  • 要过滤的参数,其可选值是如何获取的?
  • 规格过滤的可选值,其数据格式怎样的?

什么情况下显示有关规格参数的过滤?

如果用户尚未选择商品分类,或者聚合得到的分类数大于1,那么就没必要进行规格参数的聚合。因为不同分类的商品,其规格是不同的。

因此,我们在后台需要对聚合得到的商品分类数量进行判断,如果等于1,我们才继续进行规格参数的聚合

如何知道哪些规格需要过滤?

我们不能把数据库中的所有规格参数都拿来过滤。因为并不是所有的规格参数都可以用来过滤,参数的值是不确定的。

值的庆幸的是,我们在设计规格参数时,已经标记了某些规格可搜索,某些不可搜索。

因此,一旦商品分类确定,我们就可以根据商品分类查询到其对应的规格,从而知道哪些规格要进行搜索。

要过滤的参数,其可选值是如何获取的?

虽然数据库中有所有的规格参数,但是不能把一切数据都用来供用户选择。

与商品分类和品牌一样,应该是从用户搜索得到的结果中聚合,得到与结果品牌的规格参数可选值。

规格过滤的可选值,其数据格式怎样的?

我们直接看页面效果:

在这里插入图片描述
在这里插入图片描述

我们之前存储时已经将数据分段,恰好符合这里的需求

3.3.实战

接下来,我们就用代码实现刚才的思路。

总结一下,应该是以下几步:

  • 1)用户搜索得到商品,并聚合出商品分类
  • 2)判断分类数量是否等于1,如果是则进行规格参数聚合
  • 3)先根据分类,查找可以用来搜索的规格
  • 4)对规格参数进行聚合
  • 5)将规格参数聚合结果整理后返回

3.3.1.扩展返回结果

返回结果中需要增加新数据,用来保存规格参数过滤条件。这里与前面的品牌和分类过滤的json结构类似:

代码语言:javascript
复制
[
    {
        "k":"规格参数名",
        "options":["规格参数值","规格参数值"]
    }
]

因此,在java中我们用List<Map<String, String>>来表示。

代码语言:javascript
复制
public class SearchResult extends PageResult<Goods>{

    private List<Category> categories;// 分类过滤条件

    private List<Brand> brands; // 品牌过滤条件

    private List<Map<String,String>> specs; // 规格参数过滤条件

    public SearchResult(Long total, Integer totalPage, List<Goods> items,
                        List<Category> categories, List<Brand> brands,
                        List<Map<String,String>> specs) {
        super(total, totalPage, items);
        this.categories = categories;
        this.brands = brands;
        this.specs = specs;
    }
}

3.3.2.判断是否需要聚合

首先,在聚合得到商品分类后,判断分类的个数,如果是1个则进行规格聚合:

在这里插入图片描述
在这里插入图片描述

我们将聚合的代码抽取到了一个getSpecs方法中。

3.3.3.获取需要聚合的规格参数

然后,我们需要根据商品分类,查询所有可用于搜索的规格参数:

在这里插入图片描述
在这里插入图片描述

要注意的是,这里我们需要根据id查询规格,而规格参数接口需要从商品微服务提供

3.3.4.聚合规格参数

因为规格参数保存时不做分词,因此其名称会自动带上一个.keyword后缀:

在这里插入图片描述
在这里插入图片描述

3.3.5.解析聚合结果

在这里插入图片描述
在这里插入图片描述

3.3.6.最终的完整代码

代码语言:javascript
复制
@Service
public class SearchService {

    @Autowired
    private CategoryClient categoryClient;

    @Autowired
    private GoodsClient goodsClient;

    @Autowired
    private SpecificationClient specificationClient;

    @Autowired
    private GoodsRepository goodsRepository;

    @Autowired
    private BrandClient brandClient;

    @Autowired
    private ElasticsearchTemplate elasticsearchTemplate;

    private ObjectMapper mapper = new ObjectMapper();

    private static final Logger logger = LoggerFactory.getLogger(SearchService.class);

    public SearchResult search(SearchRequest request) {
        String key = request.getKey();
        // 判断是否有搜索条件,如果没有,直接返回null。不允许搜索全部商品
        if (StringUtils.isBlank(key)) {
            return null;
        }
        // 构建查询条件
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        MatchQueryBuilder basicQuery = QueryBuilders.matchQuery("all", key).operator(Operator.AND);
        queryBuilder.withQuery(basicQuery);
        // 通过sourceFilter设置返回的结果字段,我们只需要id、skus、subTitle
        queryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{"id", "skus", "subTitle"}, null));

        // 分页
        searchWithPageAndSort(queryBuilder, request);

        // 聚合
        queryBuilder.addAggregation(AggregationBuilders.terms("brands").field("cid3"));
        queryBuilder.addAggregation(AggregationBuilders.terms("category").field("brandId"));

        // 执行查询获取结果集
        AggregatedPage<Goods> goodsPage = (AggregatedPage<Goods>) this.goodsRepository.search(queryBuilder.build());

        // 获取聚合结果集
        // 商品分类的聚合结果
        List<Category> categories =
                getCategoryAggResult(goodsPage.getAggregation("brands"));
        // 品牌的聚合结果
        List<Brand> brands = getBrandAggResult(goodsPage.getAggregation("category"));

        // 根据商品分类判断是否需要聚合
        List<Map<String, Object>> specs = new ArrayList<>();
        if (categories.size() == 1) {
            // 如果商品分类只有一个才进行聚合,并根据分类与基本查询条件聚合
            specs = getSpec(categories.get(0).getId(), basicQuery);
        }

        return new SearchResult(goodsPage.getTotalElements(), goodsPage.getTotalPages(), goodsPage.getContent(), categories, brands, specs);
    }

    /**
     * 聚合出规格参数
     *
     * @param cid
     * @param query
     * @return
     */
    private List<Map<String, Object>> getSpec(Long cid, QueryBuilder query) {
        try {
            // 不管是全局参数还是sku参数,只要是搜索参数,都根据分类id查询出来
            List<SpecParam> params = this.specificationClient.querySpecParam(null, cid, true, null);
            List<Map<String, Object>> specs = new ArrayList<>();

            NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
            queryBuilder.withQuery(query);

            // 聚合规格参数
            params.forEach(p -> {
                String key = p.getName();
                queryBuilder.addAggregation(AggregationBuilders.terms(key).field("specs." + key + ".keyword"));

            });

            // 查询
            Map<String, Aggregation> aggs = this.elasticsearchTemplate.query(queryBuilder.build(),
                    SearchResponse::getAggregations).asMap();

            // 解析聚合结果
            params.forEach(param -> {
                Map<String, Object> spec = new HashMap<>();
                String key = param.getName();
                spec.put("k", key);
                StringTerms terms = (StringTerms) aggs.get(key);
                spec.put("options", terms.getBuckets().stream().map(StringTerms.Bucket::getKeyAsString));
                specs.add(spec);
            });

            return specs;
        } catch (
                Exception e)

        {
            logger.error("规格聚合出现异常:", e);
            return null;
        }

    }

    // 构建基本查询条件
    private void searchWithPageAndSort(NativeSearchQueryBuilder queryBuilder, SearchRequest request) {
        // 准备分页参数
        int page = request.getPage();
        int size = request.getSize();

        // 1、分页
        queryBuilder.withPageable(PageRequest.of(page - 1, size));
        // 2、排序
        String sortBy = request.getSortBy();
        Boolean desc = request.getDescending();
        if (StringUtils.isNotBlank(sortBy)) {
            // 如果不为空,则进行排序
            queryBuilder.withSort(SortBuilders.fieldSort(sortBy).order(desc ? SortOrder.DESC : SortOrder.ASC));
        }
    }

    // 解析品牌聚合结果
    private List<Brand> getBrandAggResult(Aggregation aggregation) {
        try {
            LongTerms brandAgg = (LongTerms) aggregation;
            List<Long> bids = new ArrayList<>();
            for (LongTerms.Bucket bucket : brandAgg.getBuckets()) {
                bids.add(bucket.getKeyAsNumber().longValue());
            }
            // 根据id查询品牌
            return this.brandClient.queryBrandByIds(bids);
        } catch (Exception e) {
            logger.error("品牌聚合出现异常:", e);
            return null;
        }
    }

    // 解析商品分类聚合结果
    private List<Category> getCategoryAggResult(Aggregation aggregation) {
        try {
            List<Category> categories = new ArrayList<>();
            LongTerms categoryAgg = (LongTerms) aggregation;
            List<Long> cids = new ArrayList<>();
            for (LongTerms.Bucket bucket : categoryAgg.getBuckets()) {
                cids.add(bucket.getKeyAsNumber().longValue());
            }
            // 根据id查询分类名称
            List<String> names = this.categoryClient.queryNameByIds(cids);

            for (int i = 0; i < names.size(); i++) {
                Category c = new Category();
                c.setId(cids.get(i));
                c.setName(names.get(i));
                categories.add(c);
            }
            return categories;
        } catch (Exception e) {
            logger.error("分类聚合出现异常:", e);
            return null;
        }
    }
}

3.3.7.测试结果

在这里插入图片描述
在这里插入图片描述

3.4.页面渲染

3.4.1.渲染规格过滤条件

首先把后台传递过来的specs添加到filters数组:

要注意:分类、品牌的option选项是对象,里面有name属性,而specs中的option是简单的字符串,所以需要进行封装,变为相同的结构:

在这里插入图片描述
在这里插入图片描述

最后的结果:

在这里插入图片描述
在这里插入图片描述

3.4.2.展示或收起过滤条件

是不是感觉显示的太多了,我们可以通过按钮点击来展开和隐藏部分内容:

在这里插入图片描述
在这里插入图片描述

我们在data中定义变量,记录展开或隐藏的状态:

在这里插入图片描述
在这里插入图片描述

然后在按钮绑定点击事件,以改变show的取值:

在这里插入图片描述
在这里插入图片描述

在展示规格时,对show进行判断:

在这里插入图片描述
在这里插入图片描述

OK!

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 3.生成规格参数过滤
    • 3.1.谋而后动
      • 3.3.实战
        • 3.3.1.扩展返回结果
        • 3.3.2.判断是否需要聚合
        • 3.3.3.获取需要聚合的规格参数
        • 3.3.4.聚合规格参数
        • 3.3.5.解析聚合结果
        • 3.3.6.最终的完整代码
        • 3.3.7.测试结果
      • 3.4.页面渲染
        • 3.4.1.渲染规格过滤条件
        • 3.4.2.展示或收起过滤条件
    相关产品与服务
    数据库
    云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档