Solr的facet和grouping在电商商品搜索中的使用
电商网站中,搜索业务线是直接与用户交互的重要部分,除了提供基本的关键词查询之外,提供一些导航性质的条件供用户选择,能有效提高用户体验效果。
本文关注三个功能
一. 页面左侧的多级类目导航:
二. 例如上部的属性筛选:
三. 商品多规格的折叠展示:
使用solr自带的facet和grouping 能帮助我们轻松实现这样的效果。其中前两个使用的是facet。
(一)、首先是类目导航问题, 类目导航通常要显示多级类目的层次关系 以及 这次搜索下各个类目所含商品的数量。不同的电商可能商品类目设计不一样,可能只有一层或者多层类目。solr的facet能够对结果进行聚合统计,类似数据库的count(*) group by 功能。如果是要facet多层,目前有两种办法:1 使用facet.pivot; 2 预先把类目层次组合成一个字段进行facet。 第一种facet.pivot就是为了层次树而进行,可以对任意不同字段组合facet产生一颗层次树,但是在solrcloud环境下不支持。第二种功能缺点是不能动态组合不同字段,而且需要再对结果进行一次拆封聚合。
类目导航除了提供商品数量,通常还有一个交互需求是要用户点击了某个分类链接之后,查询商品范围缩小,但这个类目导航依旧不变,也就是导航不会受到查询自己影响。以前不知道的时候觉得可能需要两次查询才能满足这个需求,后来经过求索得知solr的facet也有这样的能力:需要配合fq。具体办法是,在fq中加入标记的类目查询条件,然后再facet上加入排除对应的标记
例如这样fq={!tag=xxx}field:value,相应的facet.field={!ex=xxx}fieldx。 语义就是field:value的条件生效,但facet fieldx这个项是排除了xxx代表的条件。 其中{!tag=}和{!ex=}是对应的标记,field那些参数还是按正常写法,xxx要一样,否则就排除不掉这个了。这样的排除后facet 可以同时写多组,这样我们就得到了点击类目对应类目导航不变的功能。
(二)、然后是属性筛选部分,这部分可以更直接, 将商品的所有的属性名属性值 存成以key_value形式进一个字段,比如存成一个用逗号分隔的text类型: "key1_value1,key2_value2,key3_value7,..." ;或者用multivalue形式的string类型: ["key1_value","key2_value2","key3_value7"]。对字段进行facet之后,需要再根据key_value拆分聚合成相同的属性行。key_value可以存中文串也可以存id格式,id形式的对URL友好一些。
(三)、最后介绍同款商品折叠问题,这部分和商品管理有关系,通常一款商品有多个规格,也就是SPU包含多个SKU。如果商品比较少的搜索展示SKU比较好,但如果卖的都是一些多规格的商品显然展示SPU更好。在我们的产品设计中有几个核心要求,a). 规格也会成为一种属性可以到上面说的第二部分进行筛选;b). 搜索列表折叠之后,只需展示有异图的规格,举例来说就是一个鞋子 颜色规格会有很多异图,但尺码不会有异图,如果这两个规格是5*5=25种SKU,那么折叠之后只展示5个不同颜色的小图。c) 规格属性筛选之下异图也要跟着变,也就是搜了红色就不要再展示其他颜色的规格小图了
在设计如何套用solr来搜的时候,我考虑过两个方案:1. SPU做成一个doc; 2. SKU做成doc。 第一种方案显然更直接,但为了照顾属性问题,需要把SPU做成所有属性都必须包含,否则属性筛选可能会漏掉,那么为了照顾c功能,就得手动再过滤其他规格。但有一种特殊情况不可避免,比如某两个规格的SKU正好是没有的,但是SPU搜出来过滤之后发现该SPU应该不要,那么其他功能的统计数字就不准了。因此考虑第二种方案,得使用grouping,根据各资料表明性能会有影响,但功能上abc需求可以完全覆盖到,其中的异图就是SPU下不同SKU的主图。
具体的,开启grouping之后,需要使用group.ngroups来表示SPU命中数量,另外为了让类目导航的数字显示为spu的,要开启group.facet=true,这个group.facet貌似可以指定到field上,可以为不同的字段提供不同的数字比如f.xxxx.group.facet=true 表示xxxx使用grouping后的统计值。如果false 就是doc上facet的统计值。另外注意group.facet所使用的不是一个组里只挑选一个出来facet,例如某个组G下有3种不同值xyz,facet出来每个xyz各算1。电商中同款商品基本是同一个类目下的,所以不用担心影响类目导航的统计数。
全局sort会影响不同grouping之间的排序,但不影响组内排序。{ 根据b需求,如果要获得所有异图,可能需要把全组的doc都取出来遍历,如果规格范围很大的话可能需要把大量doc都取出来,这需要大量网络solr通信传输量。如果能根据group.limit来取出异图最大可能数的doc 便有可能大大降低网络消耗。因此组内排序方式应该是 全局排序+异图排序。异图排序就是让不同规格排序时候 每次都是一组异图一组的顺序,例如上面5*5的例子,应该排出 红黄蓝绿黑红黄蓝绿黑红黄蓝绿黑红黄蓝绿黑红黄蓝绿黑的顺序,即便不是(红黄蓝绿黑)顺序 也要把这5种颜色在前五位置排出来。办法是用一个排序字段image_rating: 每种颜色的商品用另外规格id(有另外规格就用,没有就随便一个数字)来做该image_rating。} 说了半天那么复杂的考量,现实可能未必需要计较那么多。
grouping毕竟是一个消耗的操作,自solr 4.6开始增加了一个field collapse功能,可以在排序前把相同的sku过滤到只剩一个,但功能上只是一个post filter,不能做grouping那么强的能力,官方建议用在数据量较大时候的去重。我认为如果对技术要求比较高的话也十分有必要进行一番研究。
说了这么多,还是需要自己亲自各种尝试才能更好的使用solr去完成业务。solr可用的功能非常多,完成需求的路线也很多,因此有盈余的开发者应该多深入一下solr。