乐优商城(二十一)——搜索过滤

目录

四、过滤条件的筛选

4.1 保存过滤项

4.1.1 定义属性

4.1.2 绑定点击事件

4.2 后台过滤条件

4.2.1 拓展请求对象

4.2.2 添加过滤条件

4.3 页面测试

五、页面展示选择的过滤项

5.1 商品分类面包屑

5.1.1 提供查询分类接口

5.1.2 页面展示面包屑

5.2 其它过滤项

5.2.1 页面处理

5.2.2 测试

5.3 隐藏已经选择的过滤项

六、取消过滤项

七、优化

7.1 价格筛选

7.1.1 前端页面

7.1.2 后端接口

7.2 图片延迟加载

7.3 搜索系统需要优化的点(未完成)


四、过滤条件的筛选

4.1 保存过滤项

4.1.1 定义属性

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

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

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

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

4.1.2 绑定点击事件

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

  • k:过滤项的key

  • option:当前过滤项对象

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

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

刷新页面,点击后查看search.filter的属性变化:

并且,此时浏览器地址也发生了变化:

网络请求也正常发出:

4.2 后台过滤条件

4.2.1 拓展请求对象

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

4.2.2 添加过滤条件

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

现在,要把页面传递的过滤条件也传入进去。

因此不能在使用普通的查询,而是要用到BooleanQuery,基本结构是这样的:

GET /leyou/_search
{
    "query":{
        "bool":{
        	"must":{ "match": { "title": "小米手机",operator:"and"}},
        	"filter":{
                "range":{"price":{"gt":2000.00,"lt":3800.00}}
        	}
        }
    }
}

bool查询可以把各种其它查询通过must(与)、must_not(非)、should(或)的方式进行组合 。

所以要对原来的基本查询进行构造:

/**
     * 构建带过滤条件的基本查询
     * @param searchRequest
     * @return
     */
    private QueryBuilder buildBasicQueryWithFilter(SearchRequest searchRequest) {
        BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
        //基本查询条件
        queryBuilder.must(QueryBuilders.matchQuery("all",searchRequest.getKey()).operator(Operator.AND));
        //过滤条件构造器
        BoolQueryBuilder filterQueryBuilder = QueryBuilders.boolQuery();
        //整理过滤条件
        Map<String,String> filter = searchRequest.getFilter();
        for (Map.Entry<String,String> entry : filter.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            String regex = "^(\\d+\\.?\\d*)-(\\d+\\.?\\d*)$";
            if (StringUtils.isNotBlank(key)) {
                if (value.matches(regex)) {
                    Double[] nums = NumberUtils.searchNumber(value, regex);
                    //数值类型进行范围查询   lt:小于  gte:大于等于
                    filterQueryBuilder.must(QueryBuilders.rangeQuery("specs." + key).gte(nums[0]).lt(nums[1]));
                } else {
                    //商品分类和品牌要特殊处理
                    if (key != "cid3" && key != "brandId") {
                        key = "specs." + key + ".keyword";
                    }
                    //字符串类型,进行term查询
                    filterQueryBuilder.must(QueryBuilders.termQuery(key, value));
                }
            } else {
                break;
            }
        }
        //添加过滤条件
        queryBuilder.filter(filterQueryBuilder);
        return queryBuilder;
    }

range查询允许以下字符:

操作符 说明
gt 大于
gte 大于等于
lt 小于
lte 小于等于

4.3 页面测试

先不点击过滤条件,直接搜索手机:

然后点击点击一个过滤条件:

查询结果如下:

五、页面展示选择的过滤项

5.1 商品分类面包屑

当用户选择一个商品分类以后,应该在过滤模块的上方展示一个面包屑,把三级商品分类都显示出来。

用户选择的商品分类就存放在search.filter中,但是里面只有第三级分类的id:cid3

需要根据它查询出所有三级分类的id及名称

5.1.1 提供查询分类接口

CategoryController


    /**
     * 根据分类id集合查询分类名称
     * @param id
     * @return
     */
    @GetMapping("all/level/{cid3}")
    public ResponseEntity<List<Category>> queryAllCategoryLevelByCid3(@PathVariable("cid3")Long id){
        List<Category> list = categoryService.queryAllCategoryLevelByCid3(id);
        if (list == null || list.size() < 1){
            return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
        }else {
            return ResponseEntity.ok(list);
        }
    }

CategoryService

CategoryServiceImpl

    /**
     * 根据cid3查询其所有层级分类
     * @param id
     * @return
     */
    @Override
    public List<Category> queryAllCategoryLevelByCid3(Long id) {
        List<Category> categoryList = new ArrayList<>();
        Category category = this.categoryMapper.selectByPrimaryKey(id);
        while (category.getParentId() != 0){
            categoryList.add(category);
            category = this.categoryMapper.selectByPrimaryKey(category.getParentId());
        }
        categoryList.add(category);
        return categoryList;
    }

接口测试

5.1.2 页面展示面包屑

后台提供了接口,下面的问题是,在哪里去查询接口?

如果是用户点击以后,那么页面就会重新发起请求,页面刷新,那么渲染的结果就没了。

因此,应该是在页面重新加载完毕后,此时因为过滤条件中加入了商品分类的条件,所以查询的结果中只有1个分类。

判断商品分类是否只有1个,如果是,则查询三级商品分类,添加到面包屑即可。

渲染:

刷新页面:

5.2 其它过滤项

所有已选择过滤项都保存在search.filter中,因此在页面遍历并展示即可。

但这里有个问题,filter中数据的格式:

基本有四类数据:

  • 商品分类:这个不需要展示,分类展示在面包屑位置

  • 品牌:这个要展示,但是其key和值不合适,不能显示一个id在页面。需要找到其name值

  • 数值类型规格:这个展示的时候,需要把单位查询出来

  • 非数值类型规格:这个直接展示其值即可

5.2.1 页面处理

            <!--已选择过滤项-->
            <ul class="tags-choose">
                <li class="tag" v-for="(v,k) in search.filter" v-if="k !== 'cid3'" :key="k">
                    {{k === 'brandId' ? '品牌' : k}}:<span style="color:red;">{{getFilterValue(k,v)}}</span>
                    <span></span>
                    <i class="sui-icon icon-tb-close"></i>
                </li>
            </ul>
  • 判断如果 k === 'cid3'说明是商品分类,直接忽略

  • 判断k === 'brandId'说明是品牌,页面显示品牌,其它规格则直接显示k的值

  • 值的处理比较复杂,我们用一个方法getFilterValue(k,v)来处理,调用时把kv都传递

5.2.2 测试

5.3 隐藏已经选择的过滤项

现在,我们已经实现了已选择过滤项的展示,但是你会发现一个问题:

已经选择的过滤项,在过滤列表中依然存在:

这些已经选择的过滤项,应该从列表中移除。

用户选择的项保存在search.filter中:

可以通过编写一个计算属性,把filters中的 已经被选择的key过滤掉,而且当只剩下一个可选项时也过滤掉。因为如果只剩下一个可选项就没有展示的必要了。

刷新页面:

六、取消过滤项

可以够看到,每个过滤项后面都有一个小叉,当点击后,应该取消对应条件的过滤。

思路非常简单:

  • 给小叉绑定点击事件

  • 点击后把过滤项从search.filter中移除,页面会自动刷新,OK

绑定点击事件

绑定点击事件时,把k传递过去,方便删除

删除过滤项

七、优化

7.1 价格筛选

7.1.1 前端页面

价格筛选的范围直接指定

由计算属性构造价格区间

然后进行遍历,并添加点击事件:

点击事件如下:

给fiter过滤参数添加价格过滤字段

刷新页面:

7.1.2 后端接口

因为搜索过滤是在buildBasicQueryWithFilter函数中进行的,所以在构建过程中加上价格过滤即可。

因为传入后台的是字符串,所以需要处理一下:

  • 先判断是否包含”元以上“,即为最后一个价格区间

  • 如果不是,去掉最后的”元“,再以”-“分割字符串,得到区间,然后进行范围查询
  • 如果是,那么去掉最后的”元以上“,然后搜索大于最低价钱的商品

处理完成后构造范围查询:

数据库中价格的单位是分,所以在查询时要乘100

7.2 图片延迟加载

使用vue插件vue-layzed。

CDN引入,然后进行配置。

将商品渲染时img的src改为v-lazy即可:

需要准备一下图片加载中和加载失败的CSS:

加载失败的图片:

加载中的图片:

有关vue-lazyload的更多内容,请参考《vue-layload图片延迟加载》

7.3 搜索系统需要优化的点(待完成)

  • 查询规格参数部分可以添加缓存

  • 聚合计算interval变化频率极低,所以可以设计为定时任务计算(周期为天),然后缓存起来。

  • 商品图片应该采用缩略图,减少流量,提高页面加载速度

  • 图片还可以采用CDN服务器

  • sku信息应该在页面异步加载,而不是放到索引库

猜你喜欢

转载自blog.csdn.net/lyj2018gyq/article/details/83062997
今日推荐