今天聊聊elasticsearch的聚合aggregation功能。
在解释elasticsearch的时候,都喜欢将es与关系数据库做对比参照,一来大部分coder对关系数据库都有或多或少了解,基本的关系模型、select功能都清楚;二来忽略内部实现如何,就表现出来的功能而言,两者也有可比之处。将两者作对比,可以帮助es新人更好的了解、使用es。接下来就看看两者的aggregation对比如何。
假定有es有一个index,存储了某家汽车经销商的销售信息:包括车的售价、销售时间、车身颜色、生产厂商等。结构如下:
PUT cars
{
"mappings": {
"properties": {
"price":{
"type": "long"
},
"color":{
"type": "keyword"
},
"make":{
"type": "keyword"
},
"sold":{
"type": "date"
}
}
}
}
那这个index在mysql中可能就对应下面的表结构:
create table cars(price integer,color varchar(20),make varchar(50),sold datetime);
往es中写入数据:
POST /cars/_bulk
{ "index": {}}
{ "price" : 10000, "color" : "red", "make" : "honda", "sold" : "2014-10-28" }
{ "index": {}}
{ "price" : 20000, "color" : "red", "make" : "honda", "sold" : "2014-11-05" }
{ "index": {}}
{ "price" : 30000, "color" : "green", "make" : "ford", "sold" : "2014-05-18" }
{ "index": {}}
{ "price" : 15000, "color" : "blue", "make" : "toyota", "sold" : "2014-07-02" }
{ "index": {}}
{ "price" : 12000, "color" : "green", "make" : "toyota", "sold" : "2014-08-19" }
{ "index": {}}
{ "price" : 20000, "color" : "red", "make" : "honda", "sold" : "2014-11-05" }
{ "index": {}}
{ "price" : 80000, "color" : "red", "make" : "bmw", "sold" : "2014-01-01" }
{ "index": {}}
{ "price" : 25000, "color" : "blue", "make" : "ford", "sold" : "2014-02-12" }
mysql也写入数据:
insert into cars values(10000,'red','honda','2014-10-28'),(20000,'red','honda','2014-11-05'),(30000,'green','ford','2014-05-18'),(15000,'blue','toyota','2014-07-02'),(12000,'green','toyota','2014-08-19'),(20000,'red','honda','2014-11-05'),(80000,'red','bmw','2014-01-01'),(25000,'blue','ford','2014-02-12')
现在,假设我们要做个aggregation,要统计下每种颜色的车子都卖了多少。mysql里,我们可以这样写:
mysql> select count(1) from cars group by color;
+----------+
| count(1) |
+----------+
| 4 |
| 2 |
| 2 |
+----------+
3 rows in set (0.00 sec)
那么,在es中需要如何写呢?请看下面:
GET cars/_search
{
"size": 0,
"aggs": {
"cts_by_color": {
"terms": {
"field": "color",
"size": 10
}
}
}
}
{
"took" : 2,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 8,
"relation" : "eq"
},
"max_score" : null,
"hits" : [ ]
},
"aggregations" : {
"cts_by_color" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "red",
"doc_count" : 4
},
{
"key" : "blue",
"doc_count" : 2
},
{
"key" : "green",
"doc_count" : 2
}
]
}
}
}
es中通过qsl来实现。我们来一步一步分解这个qsl:
GET cars/_search ①
{
"size": 0, ②
"aggs": { ③
"cts_by_color": { ④
"terms": { ⑤
"field": "color", ⑥
"size": 10 ⑦
}
}
}
}
①:aggregation处于search检索上下文中,表明aggregation是一个search request;
②:size 0表示结果集中不显示满足条件的document记录。这里实际上是所有文档。省略了match_all的部分;
"query": {
"match_all": {}
}
③:aggs 这是一个顶层的关键字,表示是aggregation操作;
④:这是自定义的名称,目前用不到。但是不能省略;
⑤:term aggregation_type,聚合的一种类型,用于keyword类型以及其他适合buckets aggregations的类型。如果用于index的text类型的话,需要启用fielddata;
⑥:以字段color来动态生成buckets,每一个unique的值生成一个buckets;
⑦:显示前10个。
这个qsl的功能是:在所有的document上,以字段color的不同值动态的生成buckets,然后以相同color的document记录数metrics从高到低倒序,取前10个值。
这里来看看aggregation的语法图:
"aggregations" : {
"<aggregation_name>" : {
"<aggregation_type>" : {
<aggregation_body>
}
[,"meta" : { [<meta_data_body>] } ]?
[,"aggregations" : { [<sub_aggregation>]+ } ]?
}
[,"<aggregation_name_2>" : { ... } ]*
}
与mysql的sql语句对照起来,这里的buckets就相当于sql中的group by,省略的默认document计数metrics就相当于sql中的count(1)。
这就引入了es总的两个重要概念:buckets、metrics。
buckets:一个buckets是满足特定条件的document的集合,简而言之,就是对documents按照条件进行分组,属于相同组的document落入一个buckets。当进行aggregation的时候,如果document上对应field的值满足对应的条件,则此document会落入这个buckets。buckets之间可以嵌套,以形成层次关系。
metrics:就是我们的最终目的,在buckets对文档分组后,取响应的指标值。例如avg,sum,max,min等。