Elasticsearch聚合学习之二:区间聚合

本文涉及的产品
检索分析服务 Elasticsearch 版,2核4GB开发者规格 1个月
简介: 本文是《Elasticsearch聚合学习》系列的第二篇,上一篇是我们熟悉了聚合的基本操作,本篇的内容是按照区间聚合的实战操作

欢迎访问我的GitHub

这里分类和汇总了欣宸的全部原创(含配套源码): https://github.com/zq2599/blog_demos

本篇概览

  • 本文是《Elasticsearch聚合学习》系列的第二篇,上一篇是我们熟悉了聚合的基本操作,本篇的内容是按照区间聚合的实战操作;

系列文章列表

  1. 《Elasticsearch聚合学习之一:基本操作》
  2. 《Elasticsearch聚合学习之二:区间聚合》
  3. 《Elasticsearch聚合学习之三:范围限定》;
  4. 《Elasticsearch聚合学习之四:结果排序》

环境信息

  • 以下是本次实战的环境信息,请确保您的Elasticsearch可以正常运行:
  1. 操作系统:Ubuntu 18.04.2 LTS
  2. JDK:1.8.0_191
  3. Elasticsearch:6.7.1
  4. Kibana:6.7.1
  • 实战用的数据依然是一些汽车销售的记录,在第一章有详细的导入步骤,请参考操作,导入后您的es中的数据如下图:

在这里插入图片描述

本章概要

  • 本篇实战的聚合操作有以下内容:
  1. 指定字段的区间聚合;
  2. 时间字段的区间聚合;
  3. 扩展实战;
  • 接下来开始实战吧。

条形图(histogram桶)

  • 还记得terms桶么,用来将指定字段值相同的文档聚合在一个桶中,而histogram桶是将指定字段值在某个范围内的文档聚合在一个桶中,如下图所示,0-19999是一个桶,11000和15000在一个桶内,23000和31000在一个桶内,这就是histogram桶:

在这里插入图片描述

  • 以汽车销售记录为例做一次聚合查询,为售价创建histogram桶,以20000作为间隔,每个桶负责的区间如上图所示,相关的销售记录就会被放入对应的桶中,请求参数和说明如下:
GET /cars/transactions/_search
{
  "size":0,                  ---令返回值的hits对象为空
  "aggs":{                   ---聚合命令
   "price":{                 ---聚合字段名称
     "histogram": {          ---桶类型
       "field": "price",     ---指定price字段的值作为判断条件
       "interval": 20000     ---每个桶负责的区间大小为20000
     }
   } 
  }
}
  • es返回的数据和说明如下:
{
  "took" : 57,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 8,
    "max_score" : 0.0,
    "hits" : [ ]
  },
  "aggregations" : {             ---聚合结果
    "price" : {                  ---请求参数中指定的名称
      "buckets" : [              ---price桶的数据在此数组中
        {
          "key" : 0.0,           ---第一个桶,区间[0-19999],0.0是起始值
          "doc_count" : 3        ---这个区间有三个文档(price值分别是10000、12000、15000)
        },
        {
          "key" : 20000.0,       ---第二个桶,区间[20000-39999],20000.0是起始值
          "doc_count" : 4        ---这个区间有四个文档
        },
        {
          "key" : 40000.0,       ---第三个桶,区间[40000-59999],40000.0是起始值
          "doc_count" : 0        ---这个区间没有文档
        },
       ......

控制空桶是否返回

  • 在上面的返回值中,第三个桶中没有文档,在有的业务场景中,我们不需要没有数据的桶,此时可以用min_doc_count参数来控制,如果min_doc_count等于2,表示桶中最少有两条记录才会出现在返回内容中,如下所示,min_doc_count如果等于1,那么空桶就不会被es返回了:
GET /cars/transactions/_search
{
  "size":0,
  "aggs":{
   "price":{
     "histogram": {
       "field": "price",
       "interval": 20000,
       "min_doc_count": 1
     }
   } 
  }
}
  • 返回值如下所示,没有文档的桶不再出现:
{
  "took" : 16,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 8,
    "max_score" : 0.0,
    "hits" : [ ]
  },
  "aggregations" : {
    "price" : {
      "buckets" : [
        {
          "key" : 0.0,
          "doc_count" : 3
        },
        {
          "key" : 20000.0,
          "doc_count" : 4
        },
        {
          "key" : 80000.0,
          "doc_count" : 1
        }
      ]
    }
  }
}

histogram桶加metrics

  • 上面的例子返回结果只有每个桶内的文档数,也可以加入metrics对桶中的数据进行处理,例如计算每个区间内的最高价、最低价、平均售价,可以加入max、min、avg参数,如下:
GET /cars/transactions/_search
{
  "size":0,                  ---令返回值的hits对象为空
  "aggs":{                   ---聚合命令
   "price":{                 ---聚合字段名称
     "histogram": {          ---桶类型
       "field": "price",     ---指定price字段的值作为判断条件
       "interval": 20000     ---每个桶负责的区间大小为20000
     },
     "aggs": {               ---表示对桶内数据做metrics
        "max_price": {       ---指定metrics处理结果的字段名
          "max":{            ---metrics类型为max
            "field": "price" ---指定取price字段的值做最大值比较
          }
        },
        "min_price": {       ---指定metrics处理结果的字段名
          "min":{            ---metrics类型为min
            "field": "price" ---指定取price字段的值做最小值比较
          }
        },
        "avg_price": {       ---指定metrics处理结果的字段名
          "avg":{            ---metrics类型为avg
            "field": "price" ---指定取price字段的值计算平均值
          }
        }
      }
   } 
  }
}
  • es返回数据和说明如下,可见每个桶中的文档都做了三种metrics处理:
{
  "took" : 17,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 8,
    "max_score" : 0.0,
    "hits" : [ ]
  },
  "aggregations" : {             ---聚合结果
    "price" : {                  ---请求参数中指定的名称
      "buckets" : [              ---price桶的数据在此数组中
        {
          "key" : 0.0,           ---第一个区间[0-19999],0.0是起始值
          "doc_count" : 3,       ---这个区间有三条记录(price值分别是10000、12000、15000)
          "max_price" : {        ---指定的metrics结果名称
            "value" : 15000.0    ---桶中有三个文档,price字段的最大值是15000
          },
          "min_price" : {
            "value" : 10000.0    ---桶中有三个文档,price字段的最小值是10000
          },
          "avg_price" : {
            "value" : 12333.333333333334    ---桶中有三个文档,price字段的平均值是12333.333333333334
          }
        },
       ......

时间区间的桶(date_histogram)

  • 按照时间区间聚合也是常用的功能,例如在ELK上查询日志,通常都是按照时间来分段的,如下图:

在这里插入图片描述

  • histogram桶可以实现按照时间分段么?如果用毫秒数来处理,似乎是可以的,但是对年月日的处理就力不从心了,常见的时间区间处理,用date_histogram桶即可满足要求;
  • 下面就是date_histogram桶的用法:每月销售多少台汽车:
GET /cars/transactions/_search
{
  "size": 0,                    ---令返回值的hits对象为空
  "aggs": {                     ---聚合命令
    "sales": {                  ---聚合字段名称
      "date_histogram": {       ---桶类型
        "field": "sold",        ---用sold字段的值作进行时间区间判断
        "interval": "month",    ---间隔单位是月
        "format": "yyyy-MM-dd"  ---返回的数据中,时间字段格式
      },
      "aggs": {                 ---表示对桶内数据做metrics
        "max_price": {          ---指定metrics处理结果的字段名
          "max":{               ---metrics类型为max
            "field": "price"    ---指定取price字段的值做最大值比较
          }
        },
        "min_price": {          ---指定metrics处理结果的字段名
          "min":{               ---metrics类型为min
            "field": "price"    ---指定取price字段的值做最小值比较
          }
        }
      }
    }
  }
}
  • es返回数据如下,篇幅所限因此略去了头部和尾部的一些信息,只看关键的:
"aggregations" : {                           ---聚合结果
    "sales" : {                              ---请求参数中指定的名称
      "buckets" : [                          ---sales桶的数据在此数组中
        {
          "key_as_string" : "2014-01-01",    ---请求的format参数指定了key的格式
          "key" : 1388534400000,             ---真正的时间字段
          "doc_count" : 1,                   ---2014年1月份的文档数量
          "max_price" : {                    ---2014年1月的文档做了metrics类型为max的处理后,结果在此
            "value" : 80000.0                ---2014年1月的文档中,price字段的最大值
          },
          "min_price" : {                    ---2014年1月的文档做了metrics类型为min的处理后,结果在此
            "value" : 80000.0                ---2014年1月的文档中,price字段的最大值
          }
        },
        {
          "key_as_string" : "2014-02-01",
          "key" : 1391212800000,
          "doc_count" : 1,
          "max_price" : {
            "value" : 25000.0
          },
          "min_price" : {
            "value" : 25000.0
          }
        },
        ......
  • 上面的请求是以一个月作为区间的,如果想以其他时间单位作为区间又该怎么做呢?例如90天,把interval字段写成90d即可,其他粒度的时间间隔写法如下表:
表达式 含义
1y 一年(数量只能是1,例如2y不合法)
1q 一个季度(数量只能是1,例如2q不合法)
1M 一个月(数量只能是1,例如2M不合法,注意区分大写,M表示月,m表示分钟)
1w 一周(数量只能是1,例如2w不合法)
2d 两天(数量可以是整数类型)
3h 三个小时(数量可以是整数类型)
4m 四分钟(数量可以是整数类型,注意区分大写,M表示月,m表示分钟)
5s 五秒钟(数量可以是整数类型)
  • 注意:年、季度、月、周都的数量只能是1,其他粒度的数量可以是整数;
  • 例如以90天作为区间来聚合,请求参数如下:
GET /cars/transactions/_search
{
  "size": 0,
  "aggs": {
    "sales": {
      "date_histogram": {
        "field": "sold",
        "interval": "90d",     -------表示以90天作为间隔
        "format": "yyyy-MM-dd"
      },
      "aggs": {
        "max_price": {
          "max":{
            "field": "price"
          }
        },
        "min_price": {
          "min":{
            "field": "price"
          }
        }
      }
    }
  }
}

date_histogram的空桶处理

  • date_histogram也支持min_doc_count参数,和histogram桶的用法一样,对于下面的请求,es的响应中不会有空桶:
GET /cars/transactions/_search
{
  "size": 0,
  "aggs": {
    "sales": {
      "date_histogram": {
        "field": "sold",
        "interval": "1M",
        "format": "yyyy-MM-dd",
        "min_doc_count": 1
      }
    }
  }
}

扩展实战

  • 本篇的最后,来做一个略为复杂的聚合操作:按季度展示每个汽车品牌的销售总额
  • 显然,操作的第一步是按照时间区间做聚合,然后在每个桶中,将文档按照品牌做第二次聚合,第二次聚合的结果也可以理解为多个桶,每个桶中的文档,是某个平台在某个季度的销售总额;
  • 请求如下:
GET /cars/transactions/_search
{
  "size": 0,                      ---令返回值的hits对象为空
  "aggs": {                       ---聚合命令
    "sales": {                    ---聚合字段名称
      "date_histogram": {         ---桶类型为时间区间
        "field": "sold",          ---指定sold字段的值作为判断条件
        "interval": "1q",         ---区间间隔为1季度
        "format": "yyyy-MM-dd",   ---返回的桶的key,被格式化时的格式
        "min_doc_count": 1        ---空桶不返回
      },
      "aggs": {                   ---第二层桶
        "per_make_sum": {         ---聚合字段名称
          "terms": {              ---桶类型为terms
            "field": "make"       ---按照make字段聚合
          },
          "aggs": {               ---第二层桶的metrics
            "sum_price": {        ---聚合字段名称
              "sum": {            ---metrics处理,累加
                "field": "price"  ---取price字段的值累加
              }
            }
          }
        }
      }
    }
  }
}
  • 收到响应如下:
"aggregations" : {
    "sales" : {
      "buckets" : [                             ---聚合结果
        {
          "key_as_string" : "2014-01-01",       ---当前桶的key的格式化后的值
          "key" : 1388534400000,                ---当前桶的key原值
          "doc_count" : 2,                      ---当前桶中文档数
          "per_make_sum" : {                    ---第二层桶的名称
            "doc_count_error_upper_bound" : 0,
            "sum_other_doc_count" : 0,
            "buckets" : [                       ---第二层聚合结果
              {
                "key" : "bmw",                  ---聚合字段的值,这里是汽车品牌
                "doc_count" : 1,                ---桶内的文档数量
                "sum_price" : {                 ---metrics处理结果名称
                  "value" : 80000.0             ---metrics处理结果,这里是销售额累加值
                }
              },
              {
                "key" : "ford",                 ---聚合字段的值,这里是汽车品牌
                "doc_count" : 1,                ---桶内的文档数量
                "sum_price" : {                 ---metrics处理结果名称
                  "value" : 25000.0             ---metrics处理结果,这里是销售额累加值
                }
              }
            ]
          }
        },
        {
          "key_as_string" : "2014-04-01",
          "key" : 1396310400000,
          "doc_count" : 1,
          "per_make_sum" : {
            "doc_count_error_upper_bound" : 0,
            "sum_other_doc_count" : 0,
            "buckets" : [
              {
                "key" : "ford",
                "doc_count" : 1,
                "sum_price" : {
                  "value" : 30000.0
                }
              }
            ]
          }
        },
  • 至此,区间聚合的学习和实战就完成了,到目前为止,我们的操作用的都是索引中的全部数据,但是真是生产环境中,不会每次都用全部数据来做聚合,因此接下来的章节,会将聚合与查询、过滤等操作结合在一起实战;

欢迎关注阿里云开发者社区博客:程序员欣宸

学习路上,你不孤单,欣宸原创一路相伴...
相关实践学习
使用阿里云Elasticsearch体验信息检索加速
通过创建登录阿里云Elasticsearch集群,使用DataWorks将MySQL数据同步至Elasticsearch,体验多条件检索效果,简单展示数据同步和信息检索加速的过程和操作。
ElasticSearch 入门精讲
ElasticSearch是一个开源的、基于Lucene的、分布式、高扩展、高实时的搜索与数据分析引擎。根据DB-Engines的排名显示,Elasticsearch是最受欢迎的企业搜索引擎,其次是Apache Solr(也是基于Lucene)。 ElasticSearch的实现原理主要分为以下几个步骤: 用户将数据提交到Elastic Search 数据库中 通过分词控制器去将对应的语句分词,将其权重和分词结果一并存入数据 当用户搜索数据时候,再根据权重将结果排名、打分 将返回结果呈现给用户 Elasticsearch可以用于搜索各种文档。它提供可扩展的搜索,具有接近实时的搜索,并支持多租户。
相关文章
|
2月前
|
自然语言处理 Java 网络架构
elasticsearch学习三:elasticsearch-ik分词器的自定义配置 分词内容
这篇文章是关于如何自定义Elasticsearch的ik分词器配置以满足特定的中文分词需求。
155 0
elasticsearch学习三:elasticsearch-ik分词器的自定义配置 分词内容
|
1月前
|
存储 SQL 监控
|
2月前
|
JSON Java 网络架构
elasticsearch学习四:使用springboot整合 rest 进行搭建elasticsearch服务
这篇文章介绍了如何使用Spring Boot整合REST方式来搭建和操作Elasticsearch服务。
142 4
elasticsearch学习四:使用springboot整合 rest 进行搭建elasticsearch服务
|
2月前
|
自然语言处理 搜索推荐 关系型数据库
elasticsearch学习六:学习 全文搜索引擎 elasticsearch的语法,使用kibana进行模拟测试(持续更新学习)
这篇文章是关于Elasticsearch全文搜索引擎的学习指南,涵盖了基本概念、命令风格、索引操作、分词器使用,以及数据的增加、修改、删除和查询等操作。
35 0
elasticsearch学习六:学习 全文搜索引擎 elasticsearch的语法,使用kibana进行模拟测试(持续更新学习)
|
2月前
|
Web App开发 JavaScript Java
elasticsearch学习五:springboot整合 rest 操作elasticsearch的 实际案例操作,编写搜索的前后端,爬取京东数据到elasticsearch中。
这篇文章是关于如何使用Spring Boot整合Elasticsearch,并通过REST客户端操作Elasticsearch,实现一个简单的搜索前后端,以及如何爬取京东数据到Elasticsearch的案例教程。
221 0
elasticsearch学习五:springboot整合 rest 操作elasticsearch的 实际案例操作,编写搜索的前后端,爬取京东数据到elasticsearch中。
|
2月前
|
自然语言处理 Java Maven
elasticsearch学习二:使用springboot整合TransportClient 进行搭建elasticsearch服务
这篇博客介绍了如何使用Spring Boot整合TransportClient搭建Elasticsearch服务,包括项目创建、Maven依赖、业务代码和测试示例。
124 0
elasticsearch学习二:使用springboot整合TransportClient 进行搭建elasticsearch服务
|
2月前
|
存储 JSON Java
elasticsearch学习一:了解 ES,版本之间的对应。安装elasticsearch,kibana,head插件、elasticsearch-ik分词器。
这篇文章是关于Elasticsearch的学习指南,包括了解Elasticsearch、版本对应、安装运行Elasticsearch和Kibana、安装head插件和elasticsearch-ik分词器的步骤。
214 0
elasticsearch学习一:了解 ES,版本之间的对应。安装elasticsearch,kibana,head插件、elasticsearch-ik分词器。
|
3月前
|
存储 自然语言处理 关系型数据库
ElasticSearch基础3——聚合、补全、集群。黑马旅游检索高亮+自定义分词器+自动补全+前后端消息同步
聚合、补全、RabbitMQ消息同步、集群、脑裂问题、集群分布式存储、黑马旅游实现过滤和搜索补全功能
ElasticSearch基础3——聚合、补全、集群。黑马旅游检索高亮+自定义分词器+自动补全+前后端消息同步
|
4月前
|
自然语言处理 Java 关系型数据库
ElasticSearch 实现分词全文检索 - 聚合查询 cardinality
ElasticSearch 实现分词全文检索 - 聚合查询 cardinality
153 1
|
6月前
|
SQL 安全 数据挖掘
Elasticsearch如何聚合查询多个统计值,如何嵌套聚合?并相互引用,统计索引中某一个字段的空值率?语法是怎么样的?
Elasticsearch聚合查询用于复杂数据分析,包括统计空值率。示例展示了如何计算字段`my_field`非空非零文档的百分比。查询分为三步:总文档数计数、符合条件文档数计数及计算百分比。聚合概念涵盖度量、桶和管道聚合。脚本在聚合中用于动态计算。常见聚合类型如`sum`、`avg`、`date_histogram`等。组合使用可实现多值统计、嵌套聚合和空值率计算。[阅读更多](https://zhangfeidezhu.com/?p=515)
312 0
Elasticsearch如何聚合查询多个统计值,如何嵌套聚合?并相互引用,统计索引中某一个字段的空值率?语法是怎么样的?