乐优商城学习笔记十七-搜索过滤(二)


title: 乐优商城学习笔记十七-搜索过滤(二)
date: 2019-04-20 14:37:56
tags:
- 乐优商城
- java
- springboot
categories:
- 乐优商城


3.生成规格参数过滤

3.1.谋而后动

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

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

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

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

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

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

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

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

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

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

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

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

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

我们直接看页面效果:

1526805322441

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

3.3.实战

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

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

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

3.3.1.扩展返回结果

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

[
    {
        "k":"规格参数名",
        "options":["规格参数值","规格参数值"]
    }
]

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

/**
 * @Author smallmartial
 * @Date 2019/4/19
 * @Email smallmarital@qq.com
 */
@Data
public class SearchResult extends PageResult<Goods> {

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

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

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

    public SearchResult(){}

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

3.3.2.判断是否需要聚合

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

    if (categories !=null && categories.size() == 1){
         specs = buildSpecificationAgg(categories.get(0).getId(),basicQuery);

        }

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

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

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

        List<SpecParam> params = specificationClient.querySpecSpecParam(null, cid, true, null);

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

商品微服务:ly-item-interface中提供接口:

@RequestMapping("spec")
public interface SpecificationApi {

    @GetMapping("/params")
    List<SpecParam> querySpecParam(SpecParam specParam);
}

搜索服务中调用:

@FeignClient("item-service")
public interface SpecificationClient extends SpecificationApi {
}

3.3.4.聚合规格参数

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

        for (SpecParam param : params) {
            String name = param.getName();
            queryBuilder.addAggregation(AggregationBuilders.terms(name)
                    .field("specs."+name+".keyword"));
        }

3.3.5.解析聚合结果

        //解析结果
       Aggregations aggs = result.getAggregations();
        for (SpecParam param : params) {
            //规格参数名
            Map<String,Object> map = new HashMap();

            //准备map
            String name = param.getName();
            map.put("k",name);

            StringTerms terms = (StringTerms) aggs.get(name);
            map.put("options",terms.getBuckets().stream().map(b -> b.getKeyAsString()).collect(Collectors.toList()));
            specs.add(map);
        }

3.3.6.最终的代码

 /**
     * 聚合规格参数查询
     * @param cid
     * @param basicQuery
     * @return
     */
    private List<Map<String,Object>> buildSpecificationAgg(Long cid, QueryBuilder basicQuery) {
        List<Map<String, Object>> specs = new ArrayList<>();
        //查询所需要的结果
        List<SpecParam> params = specificationClient.querySpecSpecParam(null, cid, true, null);
        //聚合
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        //带上查询条件
        queryBuilder.withQuery(basicQuery);
        for (SpecParam param : params) {
            String name = param.getName();
            queryBuilder.addAggregation(AggregationBuilders.terms(name)
                    .field("specs."+name+".keyword"));
        }
        //获取结果
        AggregatedPage<Goods> result = template.queryForPage(queryBuilder.build(), Goods.class);
        // 查询
        //解析结果
       Aggregations aggs = result.getAggregations();
        for (SpecParam param : params) {
            //规格参数名
            Map<String,Object> map = new HashMap();

            //准备map
            String name = param.getName();
            map.put("k",name);
            
            StringTerms terms = (StringTerms) aggs.get(name);
            map.put("options",terms.getBuckets().stream().map(b -> b.getKeyAsString()).collect(Collectors.toList()));
            specs.add(map);
        }

        return specs;
    }

结果


1526836458716

3.4.2.展示或收起过滤条件

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

1526836575516

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

1526837203921

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

1526837300139

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

1528416266890

OK!

4.过滤条件的筛选

当我们点击页面的过滤项,要做哪些事情?

  • 把过滤条件保存在search对象中(watch监控到search变化后就会发送到后台)
  • 在页面顶部展示已选择的过滤项
  • 把商品分类展示到顶部面包屑

4.1.保存过滤项

4.1.1.定义属性

我们把已选择的过滤项保存在search中:

1526902381310

要注意,在created构造函数中会对search进行初始化,所以要在构造函数中对filter进行初始化:

1526902467385

search.filter是一个对象,结构:

{
    "过滤项名":"过滤项值"
}

4.1.2.绑定点击事件

给所有的过滤项绑定点击事件:

1526902638566

要注意,点击事件传2个参数:

  • k:过滤项的key
  • option:当前过滤项对象

在点击事件中,保存过滤项到selectedFilter

selectFilter(k, o){
    const obj = {};
    Object.assign(obj, this.search);
    if(k === 'cid3' || k === 'brandId'){
        o = o.id;
    }
    obj.filter[k] = o;
    this.search = obj;
}

另外,这里search对象中嵌套了filter对象,请求参数格式化时需要进行特殊处理,修改common.js中的一段代码:

1530442052516

我们刷新页面,点击后通过浏览器功能查看search.filter的属性变化:

1526904752818

4.2.1.拓展请求对象

我们需要在请求类:SearchRequest中添加属性,接收过滤属性。过滤属性都是键值对格式,但是key不确定,所以用一个map来接收即可。

1526910290497

4.2.2.添加过滤条件

目前,我们的基本查询是这样的:

    private QueryBuilder buildBasicQuery(SearchRequest request) {
        BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
        // 基本查询条件
        queryBuilder.must(QueryBuilders.matchQuery("all", request.getKey()).operator(Operator.AND));
        // 过滤条件构建器
        BoolQueryBuilder filterQueryBuilder = QueryBuilders.boolQuery();
        // 整理过滤条件
        Map<String, String> filter = request.getFilter();
        for (Map.Entry<String, String> entry : filter.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            // 商品分类和品牌要特殊处理
            if (key != "cid3" && key != "brandId") {
                key = "specs." + key + ".keyword";
            }
            // 字符串类型,进行term查询
            filterQueryBuilder.must(QueryBuilders.termQuery(key, value));
        }
        // 添加过滤条件
        queryBuilder.filter(filterQueryBuilder);
        return queryBuilder;
    }

总结

页面过滤部分功能未能实现,点击品牌分类无法查询。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容