1.什么是数据聚合
聚合(aggregations
)可以让我们极其方便的实现对数据的统计、分析、运算。例如:
-
什么品牌的手机最受欢迎?
-
这些手机的平均价格、最高价格、最低价格?
-
这些手机每月的销售情况如何?
实现这些统计功能的比数据库的sql要方便的多,而且查询速度非常快,可以实现近实时搜索效果。
聚合常见的有三类:
-
桶(
Bucket
)聚合:用来对文档做分组-
TermAggregation
:按照文档字段值分组,例如按照品牌值分组、按照国家分组 -
Date Histogram
:按照日期阶梯分组,例如一周为一组,或者一月为一组
-
-
度量(
Metric
)聚合:用以计算一些值,比如:最大值、最小值、平均值等-
Avg
:求平均值 -
Max
:求最大值 -
Min
:求最小值 -
Stats
:同时求max
、min
、avg
、sum
等
-
-
管道(
pipeline
)聚合:其它聚合的结果为基础做进一步运算
注意:参加聚合的字段必须是keyword、日期、数值、布尔类型
2.DSL实现聚合
与之前的搜索功能类似,我们依然先学习DSL的语法,再学习JavaAPI.
1.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
:聚合的类型,按分类聚合,所以用term
-
field
:参与聚合的字段名称 -
size
:希望返回的聚合结果的最大数量
-
-
-
2.带条件聚合
但真实场景下,用户会输入搜索条件,因此聚合必须是对搜索结果聚合。那么聚合必须添加限定条件。
例如,我想知道价格高于3000元的手机品牌有哪些,该怎么统计呢?
GET /items/_search
{
"query": {
"bool": {
"filter": [
{
"term": {
"category": "手机"
}
},
{
"range": {
"price": {
"gte": 300000
}
}
}
]
}
},
"size": 0,
"aggs": {
"brand_agg": {
"terms": {
"field": "brand",
"size": 20
}
}
}
}
3.Metric聚合
上节课,我们统计了价格高于3000的手机品牌,形成了一个个桶。现在我们需要对桶内的商品做运算,获取每个品牌价格的最小值、最大值、平均值。
这就要用到Metric
聚合了,例如stat
聚合,就可以同时获取min
、max
、avg
等结果。
我们需要从需求中分析出搜索查询的条件和聚合的目标:
-
搜索查询条件:
-
价格高于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
},
"aggs": {
"stats_meric": {
"stats": {
"field": "price"
}
}
}
}
}
}
query
部分就不说了,我们重点解读聚合部分语法。
可以看到我们在brand_agg
聚合的内部,我们新加了一个aggs
参数。这个聚合就是brand_agg
的子聚合,会对brand_agg
形成的每个桶中的文档分别统计。
-
stats_meric
:聚合名称-
stats
:聚合类型,stats是metric
聚合的一种field
:聚合字段,这里选择price
,统计价格
java中代码实现
-
@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);
}
}
标签:category,聚合,agg,brand,field,数据,ES,size
From: https://www.cnblogs.com/freps/p/18475057