Elasticsearch系列---聚合查询(一)

2020-05-29 00:00:00 数据 字段 语法 统计 聚合

### 概要


Elasticsearch的聚合查询,跟数据库的聚合查询效果是一样的,我们可以将二者拿来对比学习,如求和、求平均值、求大小等等。


### 基础概念


#### bucket


数据分组,一些数据按照某个字段进行bucket划分,这个字段值相同的数据放到一个bucket中。可以理解成Java中的Map<String, List<Object>>结构,类似于Mysql中的group by后的查询结果。


#### metric:


对一个数据分组执行的统计,比如计算大值,小值,平均值等

类似于Mysql中的max(),min(),avg()函数的值,都是在group by后使用的。


### 案例


我们还是以英文儿歌为案例背景,回顾一下索引结构:

```java

PUT /music

{

"mappings": {

"children": {

"properties": {

"id": {

"type": "keyword"

},

"author_first_name": {

"type": "text",

"analyzer": "english"

},

"author_last_name": {

"type": "text",

"analyzer": "english"

},

"author": {

"type": "text",

"analyzer": "english",

"fields": {

"keyword": {

"type": "keyword",

"ignore_above": 256

}

}

},

"name": {

"type": "text",

"fields": {

"keyword": {

"type": "keyword",

"ignore_above": 256

}

}

},

"content": {

"type": "text",

"fields": {

"keyword": {

"type": "keyword",

"ignore_above": 256

}

}

},

"language": {

"type": "text",

"analyzer": "english",

"fielddata": true

},

"tags": {

"type": "text",

"analyzer": "english"

},

"length": {

"type": "long"

},

"likes": {

"type": "long"

},

"isRelease": {

"type": "boolean"

},

"releaseDate": {

"type": "date"

}

}

}

}

}

```


#### 统计目前收录的哪种语言的歌曲多

```java

GET /music/children/_search

{

"size": 0,

"aggs": {

"song_qty_by_language": {

"terms": {

"field": "language"

}

}

}

}

```


语法解释:

- size:0 表示只要统计后的结果,原始数据不展现

- aggs:固定语法 ,聚合分析都要声明aggs

- song_qty_by_language:聚合的名称,可以随便写,建议规范命名

- terms:按什么字段进行分组

- field:具体的字段名称


响应结果如下:

```java

{

"took": 2,

"timed_out": false,

"_shards": {

"total": 5,

"successful": 5,

"skipped": 0,

"failed": 0

},

"hits": {

"total": 5,

"max_score": 0,

"hits": []

},

"aggregations": {

"song_qty_by_language": {

"doc_count_error_upper_bound": 0,

"sum_other_doc_count": 0,

"buckets": [

{

"key": "english",

"doc_count": 5

}

]

}

}

}

```


语法解释:

- hits: 由于请求时设置了size:0,hits就是空的

- aggregations:聚合查询的结果

- song_qty_by_language:请求时声明的名称

- buckets:根据指定字段查询后得到的数据分组集合,[]内的是每一个数据分组,其中key为每个bucket的对应指定字段的值,doc_count为统计的数量。


默认按doc_count降序排序。


#### 按语种统计每种歌曲的平均时长

```java

GET /music/children/_search

{

"size": 0,

"aggs": {

"lang": {

"terms": {

"field": "language"

},

"aggs": {

"length_avg": {

"avg": {

"field": "length"

}

}

}

}

}

}

```


这里演示的是两层aggs聚合查询,先按语种统计,得到数据分组,再在数据分组里算平均时长。


多个aggs嵌套语法也是如此,注意一下aggs代码块的位置即可。


#### 统计长时长、短时长等的歌曲

常用的统计:count,avg,max,min,sum,语法含义与mysql相同。

```java

GET /music/children/_search

{

"size": 0,

"aggs": {

"color": {

"terms": {

"field": "language"

},

"aggs": {

"length_avg": {

"avg": {

"field": "length"

}

},

"length_max": {

"max": {

"field": "length"

}

},

"length_min": {

"min": {

"field": "length"

}

},

"length_sum": {

"sum": {

"field": "length"

}

}

}

}

}

}

```


#### 按时长分段统计歌曲平均时长


以30秒为一段,看各段区间的平均值。


histogram语法位置跟terms一样,作范围分区,搭配interval参数一起使用

interval:30表示分的区间段为[0,30),[30,60),[60,90),[90,120)


段的闭合关系是左开右闭,如果数据在某段区间内没有,也会返回空的区间。


```java

GET /music/children/_search

{

"size": 0,

"aggs": {

"sales_price_range": {

"histogram": {

"field": "length",

"interval": 30

},

"aggs": {

"length_avg": {

"avg": {

"field": "length"

}

}

}

}

}

}

```


这种数据的结果可以用来生成柱状图或折线图。


#### 按上架日期分段统计新歌数量


按月统计


date histogram与histogram语法类似,搭配date interval指定区间间隔

extended_bounds表示大的时间范围。


```java

GET /music/children/_search

{

"size": 0,

"aggs": {

"sales": {

"date_histogram": {

"field": "releaseDate",

"interval": "month",

"format": "yyyy-MM-dd",

"min_doc_count": 0,

"extended_bounds": {

"min": "2019-10-01",

"max": "2019-12-31"

}

}

}

}

}

```


interval的值可以天、周、月、季度、年等。我们可以延伸一下,比如统计今年每个季度的新发布歌曲的点赞数量

```java

GET /music/children/_search

{

"size": 0,

"aggs": {

"sales": {

"date_histogram": {

"field": "releaseDate",

"interval": "quarter",

"format": "yyyy-MM-dd",

"min_doc_count": 0,

"extended_bounds": {

"min": "2019-01-01",

"max": "2019-12-31"

}

},

"aggs": {

"lang_qty": {

"terms": {

"field": "language"

},

"aggs": {

"like_sum": {

"sum": {

"field": "likes"

}

}

}

},

"total" :{

"sum": {

"field": "likes"

}

}

}

}

}

}

```


#### 带上过滤条件


聚合查询可以和query搭配使用,相当于mysql中where与group by联合使用


##### 查询条件

```java

GET /music/children/_search

{

"size": 0,

"query": {

"match": {

"language": "english"

}

},

"aggs": {

"sales": {

"terms": {

"field": "language"

}

}

}

}

```


##### 过滤条件

```java

GET /music/children/_search

{

"size": 0,

"query": {

"constant_score": {

"filter": {

"term": {

"language": "english"

}

}

}

},

"aggs": {

"sales": {

"terms": {

"field": "language"

}

}

}

}

```


#### global bucket查询


global:就是global bucket,会将所有的数据纳入聚合scope,不受前面的query或filter影响。


global bucket适用于同时统计指定条件的数据与全部数据的对比,如我们创造的场景:指定作者的歌与全部歌曲的点赞数量对比。


```java

GET /music/children/_search

{

"size": 0,

"query": {

"match": {

"author": "Jean Ritchie"

}

},

"aggs": {

"likes": {

"sum": {

"field": "likes"

}

},

"all": {

"global": {},

"aggs": {

"all_likes": {

"sum": {

"field": "likes"

}

}

}

}

}

}

```


#### 统计近2月,近1月的点赞数


aggs.filter针对是聚合里的数据


bucket filter:对不同的bucket下的aggs,进行filter


类似于mysql的中having语法


```java

GET /music/children/_search

{

"size": 0,

"aggs": {

"recent_60d": {

"filter": {

"range": {

"releaseDate": {

"gte": "now-60d"

}

}

},

"aggs": {

"recent_60d_likes_sum": {

"sum": {

"field": "likes"

}

}

}

},

"recent_30d": {

"filter": {

"range": {

"releaseDate": {

"gte": "now-30d"

}

}

},

"aggs": {

"recent_30d_likes_sum": {

"avg": {

"field": "likes"

}

}

}

}

}

}

```


#### 统计排序


默认按doc_count降序排序,排序规则可以改,order里面可以指定aggs的别名,如length_avg,类似于mysql的order by cnt asc。


```java

GET /music/children/_search

{

"size": 0,

"aggs": {

"group_by_lang": {

"terms": {

"field": "language",

"order": {

"length_avg": "desc"

}

},

"aggs": {

"length_avg": {

"avg": {

"field": "length"

}

}

}

}

}

}

```


### 小结


本篇主要介绍常用的聚合查询,均以示例为主,了解基本写法后可以快速阅读,有不好理解的地方,多与我们熟悉的数据库查询SQL作比较,谢谢。

专注Java高并发、分布式架构,更多技术干货分享与心得,请关注公众号:Java架构社区

相关文章