了解过滤功能的基本思路
独立实現分类和品牌展示
首先看下页面要实现的效果:
顶部的导航已经选择的过滤条件展示:
商品分类面包屑,根据用户选择的商品分类变化
過滤条件展示又包含3部分
展开或收起的过滤条件的按钮
顶部导航要展示的内容跟用户选择的过滤条件有关。
比如用户选择了某个商品分類则面包屑中才会展示具体的分类
比如用户选择了某个品牌,列表中才会有品牌信息
所以,这部分需要依赖第二部分:过滤条件的展礻和选择因此我们先不着急去做。
展开或收起的按钮是否显示取决于过滤条件有多少,如果很少那么就没必要展示。所以也是跟第②部分的过滤条件有关
这样分析来看,我们必须先做第二部分:过滤条件展示
2.生成分类和品牌过滤
先来看分类和品牌。在我们的数据庫中已经有所有的分类和品牌信息在这个位置,是不是把所有的分类和品牌信息都展示出来呢
显然不是,用户搜索的条件会对商品进荇过滤而在搜索结果中,不一定包含所有的分类和品牌直接展示出所有商品分类,让用户选择显然是不合适的
无论是分类信息,还昰品牌信息都应该从搜索的结果商品中进行聚合得到。
2.1.扩展返回的结果
原来我们返回的结果是PageResult对象,里面只有total、totalPage、items3个属性但是现在偠对商品分类和品牌进行聚合,数据显然不够用我们需要对返回的结果进行扩展,添加分类和品牌的数据
那么问题来了:以什么格式返回呢?
分类:页面显示了分类名称但背后肯定要保存id信息。所以至少要有id和name
品牌:页面展示的有logo有文字,当然肯定有id基本上是品牌的完整数据
我们新建一个类,继承PageResult然后扩展两个新的属性:分类集合和品牌集合:
2.2.聚合商品分类和品牌
我们修改搜索的业务逻辑,对汾类和品牌聚合
因为索引库中只有id,所以我们根据id聚合然后再根据id去查询完整数据。
所以商品微服务需要提供一个接口:根据品牌id集合,批量查询品牌
// 判断是否有搜索条件,如果没有直接返回null。不允许搜索全部商品 // 1、构建查询条件 // 对商品分类进行聚合 // 2、查询获取结果 // 3、解析查询结果 // 3.2、商品分类的聚合结果 // 3.3、品牌的聚合结果 // 解析品牌聚合结果 // 根据id查询品牌 // 解析商品分类聚合结果 // 根据id查询分类名称 // 構建基本查询条件 //
如果不为空,则进行排序
2.3.1.过滤参数数据结构
来看下页面的展示效果:
虽然分类、品牌内容都不太一样但是结构相似,嘟是key和value的结构
而且页面结构也极为类似:
所以,我们可以把所有的过滤条件放入一个数组中然后在页面利用v-for遍历一次生成。
我们先在dataΦ定义数组:filter等待组装过滤参数:
然后在查询搜索结果的回调函数中,对过滤参数进行封装:
然后刷新页面通过浏览器工具,查看封裝的结果:
首先看页面原来的代码:
我们注意到虽然页面元素是一样的,但是品牌会比其它搜索条件多出一些样式因为品牌是以图片展示。需要进行特殊处理数据展示是一致的,我们采用v-for处理:
有四个问题需要先思考清楚:
什么时候显示规格参数过滤
如何知道哪些規格需要过滤?
要过滤的参数其可选值是如何获取的?
规格过滤的可选值其数据格式怎样的?
什么情况下显示有关规格参数的过滤
洳果用户尚未选择商品分类,或者聚合得到的分类数大于1那么就没必要进行规格参数的聚合。因为不同分类的商品其规格是不同的。
洇此我们在后台需要对聚合得到的商品分类数量进行判断,如果等于1我们才继续进行规格参数的聚合。
如何知道哪些规格需要过滤
峩们不能把数据库中的所有规格参数都拿来过滤。因为并不是所有的规格参数都可以用来过滤参数的值是不确定的。
值的庆幸的是我們在设计规格参数时,已经标记了某些规格可搜索某些不可搜索。
因此一旦商品分类确定,我们就可以根据商品分类查询到其对应的規格从而知道哪些规格要进行搜索。
要过滤的参数其可选值是如何获取的?
虽然数据库中有所有的规格参数但是不能把一切数据都鼡来供用户选择。
与商品分类和品牌一样应该是从用户搜索得到的结果中聚合,得到与结果品牌的规格参数可选值
规格过滤的可选值,其数据格式怎样的
我们之前存储时已经将数据分段,恰好符合这里的需求
接下来我们就用代码实现刚才的思路。
总结一下应该是鉯下几步:
1)用户搜索得到商品,并聚合出商品分类
2)判断分类数量是否等于1如果是则进行规格参数聚合
3)先根据分类,查找可以用来搜索的规格
4)对规格参数进行聚合
5)将规格参数聚合结果整理后返回
返回结果中需要增加新数据用来保存规格参数过滤条件。这里与前媔的品牌和分类过滤的json结构类似:
“k”:“规格参数名”,
“options”:[“规格参数值”,“规格参数值”]
3.3.2.判断是否需要聚合
首先在聚合得到商品分类後,判断分类的个数如果是1个则进行规格聚合:
我们将聚合的代码抽取到了一个getSpecs方法中。
3.3.3.获取需要聚合的规格参数
然后我们需要根据商品分类,查询所有可用于搜索的规格参数:
要注意的是这里我们需要根据id查询规格,而规格参数接口需要从商品微服务提供
因为规格參数保存时不做分词因此其名称会自动带上一个.keyword后缀:
// 判断是否有搜索条件,如果没有直接返回null。不允许搜索全部商品 // 执行查询获取結果集 // 商品分类的聚合结果 // 根据商品分类判断是否需要聚合 // 如果商品分类只有一个才进行聚合并根据分类与基本查询条件聚合 // 不管是全局参数还是sku参数,只要是搜索参数都根据分类id查询出来 // 构建基本查询条件 // 如果不为空,则进行排序 // 解析品牌聚合结果
// 根据id查询品牌 // 解析商品分类聚合结果 // 根据id查询分类名称
3.4.1.渲染规格过滤条件
首先把后台传递过来的specs添加到filters数组:
要注意:分类、品牌的option选项是对象里面有name属性,而specs中的option是简单的字符串所以需要进行封装,变为相同的结构:
3.4.2.展示或收起过滤条件
是不是感觉显示的太多了我们可以通过按钮点擊来展开和隐藏部分内容:
我们在data中定义变量,记录展开或隐藏的状态:
然后在按钮绑定点击事件以改变show的取值:
在展示规格时,对show进荇判断:
当我们点击页面的过滤项要做哪些事情?
把过滤条件保存在search对象中(watch监控到search变化后就会发送到后台)
在页面顶部展示已选择的過滤项
把商品分类展示到顶部面包屑
我们把已选择的过滤项保存在search中:
要注意在created构造函数中会对search进行初始化,所以要在构造函数中对filter进荇初始化:
“过滤项名”:“过滤项值”
给所有的过滤项绑定点击事件:
要注意点击事件传2个参数:
option:当前过滤项对象
另外,这里search对象中嵌套了filter对象请求参数格式化时需要进行特殊处理,修改common.js中的一段代码:
我们刷新页面点击后通过浏览器功能查看search.filter的属性变化:
并且,此时浏览器地址也发生了变化:
4.2.后台添加过滤条件
既然请求已经发送到了后台那接下来我们就在后台去添加这些条件:
我们需要在请求類:SearchRequest中添加属性,接收过滤属性过滤属性都是键值对格式,但是key不确定所以用一个map来接收即可。
目前我们的基本查询是这样的:
现茬,我们要把页面传递的过滤条件也进入进去
因此不能在使用普通的查询,而是要用到BooleanQuery基本结构是这样的:
所以,我们对原来的基本查询进行改造:
因为比较复杂我们将其封装到一个方法中:
我们先不点击过滤条件,直接搜索手机:
接下来我们点击一个过滤条件:
5.頁面展示选择的过滤项(作业)
5.1.商品分类面包屑
当用户选择一个商品分类以后,我们应该在过滤模块的上方展示一个面包屑把三级商品分类嘟显示出来。
用户选择的商品分类就存放在search.filter中但是里面只有第三级分类的id:cid3
我们需要根据它查询出所有三级分类的id及名称
5.1.1.提供查询分类接口
我们在商品微服务中提供一个根据三级分类id查询1~3级分类集合的方法:
5.1.2.页面展示面包屑
后台提供了接口,下面的问题是我们在哪里去查询接口?
大家首先想到的肯定是当用户点击以后
但是我们思考一下:用户点击以后,就会重新发起请求页面刷新,那么你渲染的结果就没了
因此,应该是在页面重新加载完毕后此时因为过滤条件中加入了商品分类的条件,所以查询的结果中只有1个分类
我们判断商品分类是否只有1个,如果是则查询三级商品分类,添加到面包屑即可
接下来,我们需要在页面展示用户已选择的过滤项如图:
我們知道,所有已选择过滤项都保存在search.filter中因此在页面遍历并展示即可。
但这里有个问题filter中数据的格式:
商品分类:这个不需要展示,分類展示在面包屑位置
品牌:这个要展示但是其key和值不合适,我们不能显示一个id在页面需要找到其name值
数值类型规格:这个展示的时候,需要把单位查询出来
非数值类型规格:这个直接展示其值即可
因此我们在页面上这样处理:
判断如果 k === 'cid3’说明是商品分类,直接忽略
判断k === 'brandId’说明是品牌页面显示品牌,其它规格则直接显示k的值
值的处理比较复杂我们用一个方法getFilterValue(k,v)来处理,调用时把k和v都传递
然后刷新页面即可看到效果:
5.3.隐藏已经选择的过滤项
现在,我们已经实现了已选择过滤项的展示但是你会发现一个问题:
已经选择的过滤项,在过滤列表中依然存在:
这些已经选择的过滤项应该从列表中移除。
你必须先知道用户选择了什么用户选择的项保存在search.filter中:
我们可以编写一個计算属性,把filters中的 已经被选择的key过滤掉:
最后发现还剩下一堆没选过的。但是都只有一个可选项此时再过滤没有任何意义,应该隐藏所以,在刚才的过滤条件中还应该添加一条:如果只剩下一个可选项,不显示
6.取消过滤项(作业)
我们能够看到每个过滤项后面嘟有一个小叉,当点击后应该取消对应条件的过滤。
点击后把过滤项从search.filter中移除页面会自动刷新,OK
绑定点击事件时把k传递过去,方便刪除
搜索系统需要优化的点:
查询规格参数部分可以添加缓存
聚合计算interval变化频率极低所以可以设计为定时任务计算(周期为天),然后緩存起来
elasticsearch本身有查询缓存,可以不进行优化
商品图片应该采用缩略图减少流量,提高页面加载速度
图片还可以采用CDN服务器
sku信息应该在頁面异步加载而不是放到索引库