Elasticsearch有一个功能叫做 聚合(aggregations) ,它允许你在数据上生成复杂的分析统计。它很像SQL中的 GROUP BY 但是功能更强大。

Aggregations种类分为:

  • Metrics, Metrics 是简单的对过滤出来的数据集进行avg,max等操作,是一个单一的数值。
  • Bucket, Bucket 你则可以理解为将过滤出来的数据集按条件分成多个小数据集,然后Metrics会分别作用在这些小数据集上。

对于最后聚合出来的结果,其实我们还希望能进一步做处理,所以有了Pipline Aggregations,其实就是组合一堆的Aggregations 对已经聚合出来的结果再做处理。

Elasticsearch 从1.0开始支持aggregation,基本上有了普通SQL的聚合能力。从 2.0 开始支持 pipeline aggregation,可以支持类似SQL sub query的嵌套聚合的能力。

简单聚合的例子

内容来自:

http://es.xiaoleilu.com/010_Intro/35_Tutorial_Aggregations.html

我们找到所有职员中最大的共同点(兴趣爱好)是什么:

GET /megacorp/employee/_search
{
  "aggs": {
    "all_interests": {
      "terms": { "field": "interests" }
    }
  }
}

暂时先忽略语法只看查询结果:

{
   ...
   "hits": { ... },
   "aggregations": {
      "all_interests": {
         "buckets": [
            {
               "key":       "music",
               "doc_count": 2
            },
            {
               "key":       "forestry",
               "doc_count": 1
            },
            {
               "key":       "sports",
               "doc_count": 1
            }
         ]
      }
   }
}

我们可以看到两个职员对音乐有兴趣,一个喜欢林学,一个喜欢运动。这些数据并没有被预先计算好,它们是实时的从匹配查询语句的文档中动态计算生成的。

如果我们想知道所有姓"Smith"的人最大的共同点(兴趣爱好),我们只需要增加合适的语句既可:

GET /megacorp/employee/_search
{
  "query": {
    "match": {
      "last_name": "smith"
    }
  },
  "aggs": {
    "all_interests": {
      "terms": {
        "field": "interests"
      }
    }
  }
}

all_interests聚合已经变成只包含和查询语句相匹配的文档了:

...
  "all_interests": {
     "buckets": [
        {
           "key": "music",
           "doc_count": 2
        },
        {
           "key": "sports",
           "doc_count": 1
        }
     ]
  }

聚合也允许分级汇总。例如,让我们统计每种兴趣下职员的平均年龄:

GET /megacorp/employee/_search
{
    "aggs" : {
        "all_interests" : {
            "terms" : { "field" : "interests" },
            "aggs" : {
                "avg_age" : {
                    "avg" : { "field" : "age" }
                }
            }
        }
    }
}

虽然这次返回的聚合结果有些复杂,但任然很容易理解:

...
  "all_interests": {
     "buckets": [
        {
           "key": "music",
           "doc_count": 2,
           "avg_age": {
              "value": 28.5
           }
        },
        {
           "key": "forestry",
           "doc_count": 1,
           "avg_age": {
              "value": 35
           }
        },
        {
           "key": "sports",
           "doc_count": 1,
           "avg_age": {
              "value": 25
           }
        }
     ]
  }

该聚合结果比之前的聚合结果要更加丰富。我们依然得到了兴趣以及数量(指具有该兴趣的员工人数)的列表,但是现在每个兴趣额外拥有avg_age字段来显示具有该兴趣员工的平均年龄。

 

Aggregations 一些概念

来自: http://blog.csdn.net/dm_vincent/article/details/42387161

Buckets(桶):

满足某个条件的文档集合。

Metrics(指标):

为某个桶中的文档计算得到的统计信息。

如果是SQL,  则 SELECT COUNT(color)  FROM table GROUP BY color 这个语句中的COUNT(color)就相当于一个指标。GROUP BY color则相当于一个桶。

桶和SQL中的组(Grouping)拥有相似的概念,而指标则与COUNT(),SUM(),MAX()等相似。

桶(Buckets)

一个桶就是满足特定条件的一个文档集合:

  • 一名员工要么属于男性桶,或者女性桶。
  • 城市Albany属于New York州这个桶。
  • 日期2014-10-28属于十月份这个桶。

随着聚合被执行,每份文档中的值会被计算来决定它们是否匹配了桶的条件。如果匹配成功,那么该文档会被置入该桶中,同时聚合会继续执行。

桶也能够嵌套在其它桶中,能让你完成层次或者条件划分这些需求。比如,Cincinnati可以被放置在Ohio州这个桶中,而整个Ohio州则能够被放置在美国这个桶中。

ES中有很多类型的桶,让你可以将文档通过多种方式进行划分(按小时,按最流行的词条,按年龄区间,按地理位置,以及更多)。但是从根本上,它们都根据相同的原理运作:按照条件对文档进行划分。

指标(Metrics)

桶能够让我们对文档进行有意义的划分,但是最终我们还是需要对每个桶中的文档进行某种指标计算。分桶是达到最终目的的手段:提供了对文档进行划分的方法,从而让你能够计算需要的指标。

多数指标仅仅是简单的数学运算(比如,min,mean,max以及sum),它们使用文档中的值进行计算。在实际应用中,指标能够让你计算例如平均薪资,最高出售价格,或者百分之95的查询延迟。

将两者结合起来

一个聚合就是一些桶和指标的组合。一个聚合可以只有一个桶,或者一个指标,或者每样一个。在桶中甚至可以有多个嵌套的桶。比如,我们可以将文档按照其所属国家进行分桶,然后对每个桶计算其平均薪资(一个指标)。

因为桶是可以嵌套的,我们能够实现一个更加复杂的聚合操作:

  1. 将文档按照国家进行分桶。(桶)
  2. 然后将每个国家的桶再按照性别分桶。(桶)
  3. 然后将每个性别的桶按照年龄区间进行分桶。(桶)
  4. 最后,为每个年龄区间计算平均薪资。(指标)

此时,就能够得到每个<国家,性别,年龄>组合的平均薪资信息了。它可以通过一个请求,一次数据遍历来完成!

 

聚合的进一步演示

根据这个理论,我们再来看看聚合,我们建立一个也许对汽车交易商有所用处的聚合。

内容来自: http://blog.csdn.net/dm_vincent/article/details/42407823

数据

数据是关于汽车交易的:汽车型号,制造商,销售价格,销售时间以及一些其他的相关数据。

POST /cars/transactions/_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" }

哪种颜色车好卖

我们这次建立一个聚合:一个汽车交易商也许希望知道哪种颜色的车卖的最好。这可以通过一个简单的聚合完成。使用terms桶:

GET /cars/transactions/_search?search_type=count
{
    "aggs" : {
        "colors" : {
            "terms" : {
              "field" : "color"
            }
        }
    }
}

因为我们并不关心搜索结果,使用的search_type是count,它的速度更快。

聚合工作在顶层的aggs参数下(当然你也可以使用更长的aggregations)。

然后给这个聚合起了一个名字:colors。

最后,我们定义了一个terms类型的桶,它针对color字段。

我们为聚合起一个名字colors。命名规则是有你决定的; 聚合的响应会被该名字标记,因此在应用中你就能够根据名字来得到聚合结果,并对它们进行操作了。

比如,我们定义了一个terms类型的桶。terms桶会动态地为每一个它遇到的不重复的词条创建一个新的桶。因为我们针对的是color字段,那么terms桶会动态地为每种颜色创建一个新桶。

让我们执行该聚合来看看其结果:

{
...
   "hits": {
      "hits": []
   },
   "aggregations": {
      "colors": {
         "buckets": [
            {
               "key": "red",
               "doc_count": 4
            },
            {
               "key": "blue",
               "doc_count": 2
            },
            {
               "key": "green",
               "doc_count": 2
            }
         ]
      }
   }
}

因为我们使用的search_type为count,所以没有搜索结果被返回。 每个桶中的key对应的是在color字段中找到的不重复的词条。它同时也包含了一个doc_count,用来表示包含了该词条的文档数量。

响应包含了一个桶列表,每个桶都对应着一个不重复的颜色(比如,红色或者绿色)。每个桶也包含了“掉入”该桶中的文档数量。比如,有4辆红色的车。

前面的例子是完全实时(Real-Time)的:如果文档是可搜索的,那么它们就能够被聚合。这意味着你能够将拿到的聚合结果置入到一个图形库中来生成实时的仪表板(Dashboard)。一旦你卖出了一台银色汽车,在图形上关于银色汽车的统计数据就会被动态地更新。

 

添加一个指标(Metric)

从前面的例子中,我们可以知道每个桶中的文档数量。但是,通常我们的应用会需要基于那些文档的更加复杂的指标(Metric)。比如,每个桶中的汽车的平均价格是多少?

为了得到该信息,我们得告诉ES需要为哪些字段计算哪些指标。这需要将指标嵌套到桶中。指标会基于桶中的文档的值来计算相应的统计信息。

让我们添加一个计算平均值的指标:

GET /cars/transactions/_search?search_type=count
{
   "aggs": {
      "colors": {
         "terms": {
            "field": "color"
         },
         "aggs": {
            "avg_price": {
               "avg": {
                  "field": "price"
               }
            }
         }
      }
   }
}

我们添加了一个新的aggs层级来包含该指标。然后给该指标起了一个名字:avg_price。最后定义了该指标作用的字段为price。.

正如你所看到的,我们向前面的例子中添加了一个新的aggs层级。这个新的聚合层级能够让我们将avg指标嵌套在terms桶中。这意味着我们能为每种颜色都计算一个平均值。

同样的,我们需要给指标起一个名(avg_price)来让我们能够在将来得到其值。最后,我们指定了指标本身(avg)以及该指标作用的字段(price):

{
...
   "aggregations": {
      "colors": {
         "buckets": [
            {
               "key": "red",
               "doc_count": 4,
               "avg_price": {
                  "value": 32500
               }
            },
            {
               "key": "blue",
               "doc_count": 2,
               "avg_price": {
                  "value": 20000
               }
            },
            {
               "key": "green",
               "doc_count": 2,
               "avg_price": {
                  "value": 21000
               }
            }
         ]
      }
   }
...
}

现在,在响应中多了一个avg_price元素。

尽管得到的响应只是稍稍有些变化,但是获得的数据增加的了许多。之前我们只知道有4辆红色汽车。现在我们知道了红色汽车的平均价格是32500刀。这些数据你可以直接插入到报表中。

 

桶中的桶(Buckets inside Buckets)

当你开始使用不同的嵌套模式时,聚合强大的能力才会显现出来。在前面的例子中,我们已经知道了如何将一个指标嵌套进一个桶的,它的功能已经十分强大了。

但是真正激动人心的分析功能来源于嵌套在其它桶中的桶。现在,让我们来看看如何找到每种颜色的汽车的制造商分布信息:

GET /cars/transactions/_search?search_type=count
{
   "aggs": {
      "colors": {
         "terms": {
            "field": "color"
         },
         "aggs": {
            "avg_price": {
               "avg": {
                  "field": "price"
               }
            },
            "make": {
                "terms": {
                    "field": "make"
                }
            }
         }
      }
   }
}

此时发生了一些有意思的事情。首先,你会注意到前面的avg_price指标完全没有变化。一个聚合的每个层级都能够拥有多个指标或者桶。avg_price指标告诉了我们每种汽车颜色的平均价格。为每种颜色创建的桶和指标是各自独立的。

这个性质对你的应用而言是很重要的,因为你经常需要收集一些互相关联却又完全不同的指标。聚合能够让你对数据遍历一次就得到所有需要的信息。

另外一件重要的事情是添加了新聚合make,它是一个terms类型的桶(嵌套在名为colors的terms桶中)。这意味着我们会根据数据集创建不重复的(color, make)组合。

让我们来看看得到的响应(有省略,因为响应太长了):

{
...
   "aggregations": {
      "colors": {
         "buckets": [
            {
               "key": "red",
               "doc_count": 4,
               "make": {
                  "buckets": [
                     {
                        "key": "honda",
                        "doc_count": 3
                     },
                     {
                        "key": "bmw",
                        "doc_count": 1
                     }
                  ]
               },
               "avg_price": {
                  "value": 32500
               }
            },
...
}

该响应告诉了我们如下信息:

  • 有4辆红色汽车。
  • 红色汽车的平均价格是32500美刀。
  • 红色汽车中的3辆是Honda,1辆是BMW。

 

最后的一个修改(One Final Modification)

在继续讨论新的话题前,为了把问题讲清楚让我们对该例子进行最后一个修改。为每个制造商添加两个指标来计算最低和最高价格:

GET /cars/transactions/_search?search_type=count
{
   "aggs": {
      "colors": {
         "terms": {
            "field": "color"
         },
         "aggs": {
            "avg_price": { "avg": { "field": "price" }
            },
            "make" : {
                "terms" : {
                    "field" : "make"
                },
                "aggs" : {
                    "min_price" : { "min": { "field": "price"} },
                    "max_price" : { "max": { "field": "price"} }
                }
            }
         }
      }
   }
}

我们需要添加另一个aggs层级来进行对min和max的嵌套。

得到的响应如下(仍然有省略):

{
...
   "aggregations": {
      "colors": {
         "buckets": [
            {
               "key": "red",
               "doc_count": 4,
               "make": {
                  "buckets": [
                     {
                        "key": "honda",
                        "doc_count": 3,
                        "min_price": {
                           "value": 10000
                        },
                        "max_price": {
                           "value": 20000
                        }
                     },
                     {
                        "key": "bmw",
                        "doc_count": 1,
                        "min_price": {
                           "value": 80000
                        },
                        "max_price": {
                           "value": 80000
                        }
                     }
                  ]
               },
               "avg_price": {
                  "value": 32500
               }
            },
...

{
...
   "aggregations": {
      "colors": {
         "buckets": [
            {
               "key": "red",
               "doc_count": 4,
               "make": {
                  "buckets": [
                     {
                        "key": "honda",
                        "doc_count": 3,
                        "min_price": {
                           "value": 10000
                        },
                        "max_price": {
                           "value": 20000
                        }
                     },
                     {
                        "key": "bmw",
                        "doc_count": 1,
                        "min_price": {
                           "value": 80000
                        },
                        "max_price": {
                           "value": 80000
                        }
                     }
                  ]
               },
               "avg_price": {
                  "value": 32500
               }
            },
...

在每个make桶下,多了min和max的指标。

此时,我们可以得到如下信息:

  • 有4辆红色汽车。
  • 红色汽车的平均价格是32500美刀。
  • 红色汽车中的3辆是Honda,1辆是BMW。
  • 红色Honda汽车中,最便宜的价格为10000美刀。
  • 最贵的红色Honda汽车为20000美刀。

参考资料:

ElasticSearch Aggregations 分析
http://www.jianshu.com/p/56ad2b7e27b7

ElasticSearch Aggregation Bucket 实例分析
http://www.jianshu.com/p/643a946d6c9a

http://es.xiaoleilu.com/010_Intro/35_Tutorial_Aggregations.html

ElasticSearch 的 聚合(Aggregations)的更多相关文章

  1. Elasticsearch(8) --- 聚合查询(Metric聚合)

    Elasticsearch(8) --- 聚合查询(Metric聚合) 在Mysql中,我们可以获取一组数据的 最大值(Max).最小值(Min).同样我们能够对这组数据进行 分组(Group).那么 ...

  2. Elasticsearch(9) --- 聚合查询(Bucket聚合)

    Elasticsearch(9) --- 聚合查询(Bucket聚合) 上一篇讲了Elasticsearch聚合查询中的Metric聚合:Elasticsearch(8) --- 聚合查询(Metri ...

  3. ElasticSearch实战系列五: ElasticSearch的聚合查询基础使用教程之度量(Metric)聚合

    Title:ElasticSearch实战系列四: ElasticSearch的聚合查询基础使用教程之度量(Metric)聚合 前言 在上上一篇中介绍了ElasticSearch实战系列三: Elas ...

  4. Elasticsearch 之聚合分析入门

    本文主要介绍 Elasticsearch 的聚合功能,介绍什么是 Bucket 和 Metric 聚合,以及如何实现嵌套的聚合. 首先来看下聚合(Aggregation): 什么是 Aggregati ...

  5. Elasticsearch系列---聚合查询原理

    概要 本篇主要介绍聚合查询的内部原理,正排索引是如何建立的和优化的,fielddata的使用,最后简单介绍了聚合分析时如何选用深度优先和广度优先. 正排索引 聚合查询的内部原理是什么,Elastich ...

  6. ElasticSearch - 信息聚合系列之聚合过滤

    摘要 聚合范围限定还有一个自然的扩展就是过滤.因为聚合是在查询结果范围内操作的,任何可以适用于查询的过滤器也可以应用在聚合上. 版本 elasticsearch版本: elasticsearch-2. ...

  7. (转)Elasticsearch分析聚合

    Elasticsearch不仅仅适合做全文检索,分析聚合功能也很好用.下面通过实例来学习. 一.准备数据 {"index":{ "_index": " ...

  8. Elasticsearch学习(4) spring boot整合Elasticsearch的聚合操作

    之前已将spring boot原生方式介绍了,接下将结介绍的是Elasticsearch聚合操作.聚合操作一般来说是解决一下复杂的业务,比如mysql中的求和和分组,由于博主踩的坑比较多,所以博客可能 ...

  9. ES 24 - 如何通过Elasticsearch进行聚合检索 (分组统计)

    目录 1 普通聚合分析 1.1 直接聚合统计 1.2 先检索, 再聚合 1.3 扩展: fielddata和keyword的聚合比较 2 嵌套聚合 2.1 先分组, 再聚合统计 2.2 先分组, 再统 ...

随机推荐

  1. #linux包之sysstat之iostat命令

    概述 对于I/O-bond类型的进程,我们经常用iostat工具查看进程IO请求下发的数量.系统处理IO请求的耗时,进而分析进程与操作系统的交互过程中IO方面是否存在瓶颈.同vmstat一样,iost ...

  2. 【转】C#综合揭秘——通过修改注册表建立Windows自定义协议

    引言 本文主要介绍注册表的概念与其相关根项的功能,以及浏览器如何通过连接调用自定义协议并与客户端进行数据通信.文中讲及如何通过C#程序.手动修改.安装项目等不同方式对注册表进行修改.其中通过安装项目对 ...

  3. html之ul标签

    html之无序列表,建议使用样式来定义列表的类型. 通常和li配对使用 可选属性: type:disc 圆点,circle圆圈,square方块 compact:显示效果比正常更小巧 <body ...

  4. 深入理解HashMap

    转自:http://annegu.iteye.com/blog/539465 Hashmap是一种非常常用的.应用广泛的数据类型,最近研究到相关的内容,就正好复习一下.网上关于hashmap的文章很多 ...

  5. 使用 Windows PowerShell 来管理和开发 windowsazure.cn 账户的特别注意事项

    6月6日,微软面向中国大陆用户开放了Microsoft Azure公众预览版的申请界面.大家可以申请免费的 beta 试用,收到内附邀请码的通知邮件后只需输入激活码即可开始免费试用.具体网址为: ht ...

  6. linux下进程、端口号相互查看方法

    linux下通过进程名查看其占用端口: 1.先查看进程pid ps -ef | grep 进程名 2.通过pid查看占用端口 netstat -nap | grep 进程pid 例:通过nginx进程 ...

  7. Puppet's Commands 3.7

    Puppet's Commands Puppet’s command line interface consists of a single puppet command with many subc ...

  8. 一款灵活好用的日历控件Kalendae

    Kalendae是一款纯js不依赖任何js库的日历控件,可以轻松实现显示月份数量,当前选中多个日期,并可以按照周等你想要的格式去定制选中项. 下载地址:GitHub/Kalendae 第一步:Kale ...

  9. [dts]Device Tree格式解析

    转自:http://blog.csdn.net/airk000/article/details/21345159 目录: 1. 作用 2. 基本数据格式 3. 一些基本概念 4. 工作方式 a. 地址 ...

  10. 打开PDF文件弹出阅读未加标签文档的解决方法

    在“高级”菜单的“辅助工具”选中“设置助手”,然后点选“设置屏幕阅读器选项”,下一步之后,将“忽略已加标签文档的阅读顺序”和“添加标签到文档之前进行确认”(有的版本显示的是“为文档加标签前确认”)前面 ...