完整教程:ES数据聚合
用处:
聚合(aggregations)可以极其方便的实现对数据的统计、分析、运算。例如:
- 什么品牌的手机最受欢迎?
- 这些手机的平均价格、最高价格、最低价格?
- 这些手机每月的销售情况如何?
实现这些统计功能的比数据库的sql要方便的多,而且查询速度非常快,可以实现近实时搜索效果。
官方文档:
Aggregations | Elasticsearch Guide [7.12] | Elastic
聚合常见的有三类:
- 桶(
**Bucket****)**聚合:用来对文档做分组TermAggregation:按照文档字段值分组,例如按照品牌值分组、按照国家分组Date Histogram:按照日期阶梯分组,例如一周为一组,或者一月为一组
- 度量(
**Metric****)**聚合:用以计算一些值,比如:最大值、最小值、平均值等Avg:求平均值Max:求最大值Min:求最小值Stats:同时求max、min、avg、sum等
- 管道(
**pipeline****)**聚合:其它聚合的结果为基础做进一步运算
:::color5
**注意:**参加聚合的字段必须是keyword、日期、数值、布尔类型
:::
DSL实现聚合
Bucket聚合
例如要统计所有商品中共有哪些商品分类,其实就是以分类(category)字段对数据分组。category值一样的放在同一组,属于Bucket聚合中的Term聚合。
基本语法:
GET /items/_search
{
"size": 0
,
"aggs": {
"category_agg": {
"terms": {
"field": "category"
,
"size": 20
}
}
}
}
语法说明:
size:设置size为0,就是每页查0条,则结果中就不包含文档,只包含聚合aggs:定义聚合category_agg:聚合名称,自定义,但不能重复terms:聚合的类型,按分类聚合,所以用termfield:参与聚合的字段名称size:希望返回的聚合结果的最大数量
来看下查询的结果:

带条件聚合
- 搜索查询条件:
- 价格高于3000
- 必须是手机
- 聚合目标:统计的是品牌,肯定是对brand字段做term聚合
语法如下:
GET /items/_search
{
"query": {
"bool": {
"filter": [
{
"term": {
"category": "手机"
}
}
,
{
"range": {
"price": {
"gte": 300000
}
}
}
]
}
}
,
"size": 0
,
"aggs": {
"brand_agg": {
"terms": {
"field": "brand"
,
"size": 20
}
}
}
}
聚合结果如下:
{
"took" : 2
,
"timed_out" : false
,
"hits" : {
"total" : {
"value" : 13
,
"relation" : "eq"
}
,
"max_score" :
null
,
"hits" : [ ]
}
,
"aggregations" : {
"brand_agg" : {
"doc_count_error_upper_bound" : 0
,
"sum_other_doc_count" : 0
,
"buckets" : [
{
"key" : "华为"
,
"doc_count" : 7
}
,
{
"key" : "Apple"
,
"doc_count" : 5
}
,
{
"key" : "小米"
,
"doc_count" : 1
}
]
}
}
}
Metric聚合
需要对桶内的商品做运算,获取每个品牌价格的最小值、最大值、平均值。
这就要用到Metric聚合了,例如stat聚合,就可以同时获取min、max、avg等结果。
语法如下:
GET /items/_search
{
"query": {
"bool": {
"filter": [
{
"term": {
"category": "手机"
}
}
,
{
"range": {
"price": {
"gte": 300000
}
}
}
]
}
}
,
"size": 0
,
"aggs": {
"brand_agg": {
"terms": {
"field": "brand"
,
"size": 20
}
,
"aggs": {
"stats_meric": {
"stats": {
"field": "price"
}
}
}
}
}
}
query部分就不说了,我们重点解读聚合部分语法。
可以看到在brand_agg聚合的内部,新加了一个aggs参数。这个聚合就是brand_agg的子聚合,会对brand_agg形成的每个桶中的文档分别统计。
stats_meric:聚合名称stats:聚合类型,stats是metric聚合的一种field:聚合字段,这里选择price,统计价格
由于stats是对brand_agg形成的每个品牌桶内文档分别做统计,因此每个品牌都会统计出自己的价格最小、最大、平均值。
结果如下:

另外,还可以让聚合按照每个品牌的价格平均值排序:

RestClient实现聚合
可以看到在DSL中,aggs聚合条件与query条件是同一级别,都属于查询JSON参数。因此依然是利用request.source()方法来设置。
不过聚合条件的要利用AggregationBuilders这个工具类来构造。DSL与JavaAPI的语法对比如下:

聚合结果与搜索文档同一级别,因此需要单独获取和解析。具体解析语法如下:

完整代码如下:
@Test
void testAgg(
)
throws IOException {
// 1.创建Request
SearchRequest request =
new SearchRequest("items"
)
;
// 2.准备请求参数
BoolQueryBuilder bool = QueryBuilders.boolQuery(
)
.filter(QueryBuilders.termQuery("category"
, "手机"
)
)
.filter(QueryBuilders.rangeQuery("price"
).gte(300000
)
)
;
request.source(
).query(bool).size(0
)
;
// 3.聚合参数
request.source(
).aggregation(
AggregationBuilders.terms("brand_agg"
).field("brand"
).size(5
)
)
;
// 4.发送请求
SearchResponse response = client.search(request, RequestOptions.DEFAULT
)
;
// 5.解析聚合结果
Aggregations aggregations = response.getAggregations(
)
;
// 5.1.获取品牌聚合
Terms brandTerms = aggregations.get("brand_agg"
)
;
// 5.2.获取聚合中的桶
List<
?
extends Terms.Bucket> buckets = brandTerms.getBuckets(
)
;
// 5.3.遍历桶内数据
for (Terms.Bucket bucket : buckets) {
// 5.4.获取桶内key
String brand = bucket.getKeyAsString(
)
;
System.out.print("brand = " + brand)
;
long count = bucket.getDocCount(
)
;
System.out.println("; count = " + count)
;
}
}
总结
aggs代表聚合,与query同级,query的作用是:
- 限定聚合的的文档范围
聚合必须的三要素:
- 聚合名称
- 聚合类型
- 聚合字段
聚合可配置属性有:
- size:指定聚合结果数量
- order:指定聚合结果排序方式
- field:指定聚合字段
浙公网安备 33010602011771号