1.需求
我们网站要实现全文检索的功能,业务是这样的,有两个对象,一个课程包,一个视频,课程包下可以有很多个视频,相当于是1对多的关系,全文检索的关键词是根据视频名去检索的,而不是课程包名称,但是搜索的结果页展示的是课程包列表(分页),如果用关系型数据库,比如mysql,那么一般查询sql看起来应该l是这样的:
select * from t_package_video where video_name like '%xxx%' group by package_id limit 0,10
在elasticsearch中group by就相当于是聚合中的桶,例如把人类的肤色进行聚合,那么结果出来的就是3个桶,黄桶,白桶和黑桶。
但是elasticsearch 中的聚合是不支持聚合后分页的,就比如我上面这个需求是没有办法直接实现。
2.解决
2.1.通过聚合中的size属性解决(分不了页)
我是利用springboot集成的elasticsearch,所以他的代码是这样的:
SearchQuery searchQuery = queryBuilder.withQuery(boolQuery)
.withSort(new ScoreSortBuilder())
.addAggregation(AggregationBuilders.terms("packageId").field("packageId").size(1))
.build();
terms表示桶聚合,聚合目前有4中,桶聚合只是其中一种,可以去官网查询。
其中的参数就是给这个聚合赋予一个名字,而field则是指定对索引中的哪个字段进行聚合,我这里就是packageId.
后面的size属性非常重要,这个是指桶的个数,就是你要返回几个桶,这个参数就可以当做是你的pageSize使用,但是却指定不了页码,因为方法里面没有from等指定页码或者开始坐标的方法。当你获取返回值后,每个桶就是你要的一条数据,通过每个桶的key查找出该key的第一条记录
2.2. 改变索引的字段结构(完美解决,但是不适合所有的情况)
我一开始设置的索引字段就是,packageId,videoName即最常见的逻辑,假设一个课程包下有3个视频,那么就会被添加3个document,但是这种结构就会出现我们上面说的聚合后分不了页的情况,所以我换了一种结构,即packageId,videoNames,注意多了一个s,即将3个视频的名称合在一起,这样的话,我就不用去做聚合操作了,直接分页就行了,但是这种方法不是万能的,因为我的搜索结果页只需要课程包的信息,并不需要视频的名称或者视频的其他信息,所以我可以合并视频名,但是一细想,就算需要每个课程包下的视频信息又怎样,我通过这种方式已经获取了分页的课程包列表,我再在数据库中查出每个课程包下的视频也是可以的,所以只要灵活变通,应该都没啥问题