前文如上:
9.工具使用:Elasticsearch从入门到放弃(1)-Elasticsearch概念篇
10.工具使用:Elasticsearch从入门到放弃(2)-相关性算法
11.工具使用:Elasticsearch从入门到放弃(3)-权重及打分 (qq.com)
1.【Elasticsearch】Elasticsearch从入门到放弃-Elasticsearch概念篇
2.【Elasticsearch】Elasticsearch从入门到放弃-相关性算法
3.【Elasticsearch】Elasticsearch从入门到放弃-权重及打分
4.【Elasticsearch】Elasticsearch从入门到放弃-聚合概述
4. 聚合方式
ES支持灵活的聚合方式,它不仅支持聚合和查询相结合,而且还可以使聚合的过滤条件不影响搜索条件,并且还支持在聚合后的结果中进行过滤筛选。本节将介绍这些聚合方式。
4.1 直接聚合
直接聚合指的是聚合时的DSL没有query子句,是直接对索引内的所有文档进行聚合。前面介绍的示例都属于直接聚合,这里不再进行演示。
4.2 先查询再聚合
以下代码演示了在Java中先查询再聚合的逻辑:
4.3 前过滤器
有时需要对聚合条件进一步地过滤,但是又不能影响当前的查询条件。例如
用户进行酒店搜索时的搜索条件是天津的酒店,但是聚合时需要将非满房的酒店平均价格进行聚合并展示给用户。此时不能变更用户的查询条件,需要在聚合子句中添加过滤条件
。下面的DSL展示了在聚合时使用过滤条件的用法:
执行上述DSL后,ES返回的结果如下:
通过上述结果可以知道,满足查询条件的文档个数为2,命中的文档为004和005,
但是在聚合时要求匹配非满房的酒店,只有文档004满足聚合条件,因此酒店的平均值为文档004的price字段值
。
以下代码演示了在Java中使用前过滤器的逻辑:
(注意两个聚合之间前置过滤 满房状态和价格有父子关系)
4.4 后过滤器
在有些场景中,需要根据条件进行数据查询,但是聚合的结果集不受影响。例如
在酒店搜索场景中,用户的查询词为“假日”,此时应该展现标题中带有“假日”的酒店。但是在该页面中,如果还希望给用户呈现出全国各个城市的酒店的平均价格,这时可以使用ES提供的后过滤器功能。该过滤器是在查询和聚合之后进行过滤的,因此它的过滤条件对聚合没有影响。
以下的DSL展示了后过滤器的使用:
在上面的查询中,使用match匹配title中包含“假日”的酒店,并且查询出这些酒店的平均价格,
最后使用post_filter设置后过滤器的条件,将酒店的城市锁定为“北京”
(title包含假日的有四个文档,把找到城市在北京的,只有三个,一个天津的被排除掉了,算平均价格)。执行该DSL后,ES返回的结果如下:
以下代码演示了在Java中使用后过滤器的逻辑:
5.聚合排序
根据前面的介绍可知,ES对于聚合结果的默认排序规则有时并非是我们期望的。可以使用ES提供的sort子句进行自定义排序,有多种排序方式供用户选择:
可以按照聚合后的文档计数的大小进行排序;可以按照聚合后的某个指标进行排序;还可以按照每个组的名称进行排序
。下面将介绍以上3种排序功能。
5.1 按文档计数排序
在聚合排序时,业务需求可能有按照每个组聚合后的文档数量进行排序的场景。此时可以使用_count来引用每组聚合的文档计数进行排序。以下DSL演示了按照城市的酒店平均价格进行聚合,并按照聚合后的文档计数进行升序排列的请求:
执行上述DSL后,ES返回的结果如下:
以下代码演示了在Java中使用文档计数进行聚合排序的逻辑:
5.2 按聚合指标排序
在聚合排序时,业务需求可能有按照每个组聚合后的指标值进行排序的场景。此时可以使用指标的聚合名称来引用每组聚合的文档计数。以下DSL
演示了按照城市的酒店平均价格进行聚合,并按照聚合后的平均价格进行升序排列的请求
:
执行上述DSL后,ES返回的结果如下:
以下代码演示了在Java中按照聚合指标进行聚合排序的逻辑:
(城市和平均价格是父子关系)
5.3 按分组key排序
在聚合排序时,业务需求可能有按照每个分组的组名称排序的场景。此时可以使用_key来引用分组名称。以下DSL演示了
按照城市的酒店平均价格进行聚合,并按照聚合后的分组名称进行升序排列
的请求:
执行上述DSL后,ES返回的结果如下:
以下代码演示了在Java中按照分组key进行聚合排序的逻辑::
(城市和平均价格是父子关系)
6. 聚合分页
ES支持同时返回查询结果和聚合结果,前面介绍聚合查询时,查询结果和聚合结果各自封装在不同的子句中。但
有时我们希望聚合的结果按照每组选出前N个文档的方式进行呈现
,最常见的一个场景就是电商搜索,如搜索苹果手机6S,搜索结果应该展示苹果手机6S型号中的一款手机即可,而不论该型号手机的颜色有多少种。另外,当聚合结果和查询结果封装在一起时,还需要考虑对结果分页的问题,此时前面介绍的聚合查询就不能解决这些问题了。
ES提供的Top hits聚合和Collapse聚合可以满足上述需求,但是这两种查询的分页方案是不同的。本节将介绍Top hits聚合和Collapse聚合,并分别给出这两种查询的分页方案
。
6.1 Top hits聚合
顾名思义,
Top hits聚合指的是聚合时在每个分组内部按照某个规则选出前N个文档进行展示
。例如,搜索“金都”时,如果希望
按照城市分组,每组按照匹配分数降序展示3条文档
数据,DSL如下:
执行上述查询后,ES返回的结果如下:
可以看到,在索引中一共有3个文档命中match查询条件,在聚合结果中按照城市分成了两个组“北京”“天津”,在“北京”下面有两个文档命中,并且
按照得分将展示文档进行了降序排列
,“天津”只有一个文档命中。
Top hits聚合能满足“聚合的结果按照每组选出N个文档的方式进行呈现”的需求,但是很遗憾,它不能完成自动分页功能。
如果在聚合中使用Top hits聚合并期望对数据进行分页,则要求聚合的结果一定不能太多,因为需要由客户端自行进行分页,此时对分页内存的存储能力是一个挑战。可以一次性获取聚合结果并将其存放在内存中或者Redis中,然后自行实现翻页逻辑,完成翻页。假设数据一次性存储到Redis的list结构中,以下示例代码演示了从Redis分页取数据的逻辑:
6.2 Collapse聚合
如前面所述,当
在索引中有大量数据命中时,Top hits聚合存在效率问题,并且需要用户自行排序
。针对上述问题,ES推出了Collapse聚合,即用户可以在collapse子句中指定分组字段,匹配query的结果按照该字段进行分组,并在每个分组中按照得分高低展示组内的文档。当用户在query子句外指定from和size时,将作用在Collapse聚合之后,即此时的分页是作用在分组之后的。以下DSL展示了Collapse聚合的用法:
执行上述DSL后,ES返回的结果如下:
从结果中可以看到,与Top hits聚合不同,Collapse聚合的结果是封装在hit中的。在索引中一共有3个文档命中match查询条件,在聚合结果中已经按照城市分成了两个组,即“北京”“天津”,在“北京”下面有两个文档命中,其中得分最高的文档为003,“天津”只有一个文档命中。上述结果不仅能按照得分进行排序,并且具备分页功能。
以下代码演示了在Java中使用Collapse聚合的逻辑: