关于后端:es笔记七之聚合操作之桶聚合和矩阵聚合

40次阅读

共计 8373 个字符,预计需要花费 21 分钟才能阅读完成。

本文首发于公众号:Hunter 后端
原文链接:es 笔记七之聚合操作之桶聚合和矩阵聚合

桶 (bucket) 聚合并不像指标 (metric) 聚合一样在字段上计算,而是会创立数据的桶,咱们能够了解为分组,依据某个字段进行分组,将符合条件的数据分到同一个组里。

桶聚合能够有子聚合,意思就是在分组之后,能够在每个组里再次进行聚合操作,聚合的数据就是每个组的数据。

以下是本篇笔记目录:

  1. 根本桶聚合操作
  2. 过滤聚合
  3. 多桶过滤聚合
  4. 全局聚合
  5. 直方图聚合
  6. 嵌套聚合
  7. 范畴聚合
  8. 罕见词聚合
  9. 矩阵聚合

1、根本桶聚合操作

咱们能够简略的先来进行一下桶聚合的操作,比方咱们依据 age 字段对数据进行分组操作:

GET /bank/_search
{
  "size": 0,
  "aggs": {
    "bucket_age": {
      "terms": {
        "field": "age",
        "size": 20
      }
    }
  }
}

返回的数据如下:

{
  ...
  "aggregations" : {
    "bucket_age" : {
      "doc_count_error_upper_bound" : 0,
      "sum_other_doc_count" : 35,
      "buckets" : [
        {
          "key" : 31,
          "doc_count" : 61
        },
        {
          "key" : 39,
          "doc_count" : 60
        },
        {
          "key" : 26,
          "doc_count" : 59
        },
        ...
      ]
    }
  }
}     

所有的数据在 aggregations.bucket_age.buckets 下,这是一个数组,key 的内容为 age 的值,doc_count 为该 age 值的数据条数。

其中,bucket_age 为咱们定义的桶聚合的名称。

接下来咱们介绍桶聚合和指标聚合的其余操作。

2、过滤聚合

如果咱们想针对某特定的数据进行聚合,那么就波及数据的过滤,筛选出特定的数据进行聚合。

比方咱们想筛选出 gender 的值为 “F” 的数据,而后对其进行取平均数的操作,咱们能够应用 filter 来如下操作:

GET /bank/_search
{
  "size": 0,
  "aggs": {
    "bucket_gender": {"filter": {"term": {"gender.keyword": "F"}},
      "aggs": {"avg_balance": {"avg": {"field": "balance"}}
      }
    }
  }
}

aggs.bucket_gender 咱们应用 filter 对数据进行了一个过滤,筛选出 gender 的值为 “F” 的数据。

留神,在这里,因为咱们写入数据前,没有事后定义字段的类型,所以 es 中将其主动转化成 text 属性的字段,所以在查问的时候用到的是 gender.keyword,意思是对 gender 字段的内容作为整体进行筛选。

如果自身是 keyword 属性,就不必加 .keyword 来操作。

与 filter 同级的 aggs,进行针对筛选出的数据进行聚合的操作,这里咱们用到的是平均值。

返回的数据如下:

  ...
  "aggregations" : {
    "bucket_gender" : {
      "doc_count" : 493,
      "avg_balance" : {"value" : 25623.34685598377}
    }
  }
}

3、多桶过滤聚合

在上一点咱们过滤的是单个条件,gender=’F’ 的状况,如果咱们想要实现多个过滤来操作,能够应用 filters,应用办法也不一样。

比方咱们想别离对 gender 的值为 F 和 M 的数据进行均值操作,咱们能够一步步来操作,咱们先来通过 filters 实现两个桶的聚合:

GET /bank/_search
{
  "size": 0,
  "aggs": {
    "bucket_gender": {
      "filters": {
        "filters": {"female": {"term": {"gender.keyword": "F"}},
          "male": {"term": {"gender.keyword": "M"}}
        }
      }
    }
  }
}

返回的数据就是两个桶,蕴含了两类数据的总数:

  ...
  "aggregations" : {
    "bucket_gender" : {
      "buckets" : {
        "female" : {"doc_count" : 493},
        "male" : {"doc_count" : 507}
      }
    }
  }
}

如果想在此基础上接着对其进行均值计算,和后面的 filter 操作一样,在第一个 filters 同级的中央,加上咱们的指标聚合操作:

GET /bank/_search
{
  "size": 0,
  "aggs": {
    "bucket_gender": {
      "filters": {
        "filters": {"female": {"term": {"gender.keyword": "F"}},
          "male": {"term": {"gender.keyword": "M"}}
        }
      },
      "aggs": {"avg_balance": {"avg": {"field": "balance"}}
      }
    }
  }
}

这样,在返回的桶的数据之内,还蕴含了一个均值的后果:

  ...
  "aggregations" : {
    "bucket_gender" : {
      "buckets" : {
        "female" : {
          "doc_count" : 493,
          "avg_balance" : {"value" : 25623.34685598377}
        },
        "male" : {
          "doc_count" : 507,
          "avg_balance" : {"value" : 25803.800788954635}
        }
      }
    }
  }
}

这里咱们因为 gender 只有 F 和 M 两个值,所以没有第三类数据,对于其余数据,比方 age,有很多值,除了某几种特定的值外,咱们还想获取剩下的值的信息,如何操作呢?

这里应用到 other_bucket_key 这个参数,比方咱们除了定义的 female 和 male,咱们还定义一个 non_gender 字段来统计非 M 和 F 的值,咱们能够这样操作:

GET /bank/_search
{
  "size": 0,
  "aggs": {
    "bucket_gender": {
      "filters": {
        "other_bucket_key": "non_gender", 
        "filters": {"female": {"term": {"gender.keyword": "F"}},
          "male": {"term": {"gender.keyword": "M"}}
        }
      }
    }
  }
}

返回的值如下:

  ...
  "aggregations" : {
    "bucket_gender" : {
      "buckets" : {
        "female" : {
          "doc_count" : 493,
          "avg_balance" : {"value" : 25623.34685598377}
        },
        "male" : {
          "doc_count" : 507,
          "avg_balance" : {"value" : 25803.800788954635}
        },
        "non_gender" : {
          "doc_count" : 0,
          "avg_balance" : {"value" : null}
        }
      }
    }
  }
}

4、全局聚合

如果咱们要在限定的范畴内进行聚合,然而又想在全局范畴内获取聚合数据进行比对。

比如说,咱们在 gender=’F’ 的范畴进行聚合操作:

GET /bank/_search
{
  "size": 0, 
  "query": {"match": {"gender.keyword": "F"}},
  "aggs": {
    "female_balance_avg": {
      "avg": {"field": "balance"}
    }
  }
}

这里通过 query 操作筛选 gender=’F’ 的数据,而后对 balance 字段进行聚合,如果同时咱们想要获取所有数据的 balance 的平均值,咱们能够应用 global 来操作,如下:

GET /bank/_search
{
  "size": 0, 
  "query": {"match": {"gender.keyword": "F"}},
  "aggs": {
    "total_balance_avg": {"global": {},
      "aggs": {
        "avg_balance": {"avg": {"field": "balance"}
        }
      }
    },
    "female_balance_avg": {
      "avg": {"field": "balance"}
    }
  }
}

这样就有两个数据来比对,后果如下:

  ...
  "aggregations" : {
    "female_balance_avg" : {"value" : 25623.34685598377},
    "total_balance_avg" : {
      "doc_count" : 1000,
      "avg_balance" : {"value" : 25714.837}
    }
  }
}

5、直方图聚合

这是个相似于直方图的区间桶的聚合操作。

比方对于 age 字段,咱们想以 5 为步长进行聚合,如果 age 字段在 20-50 之间,那么返回的数据就会相似于 20-24,25-29,30-34… 以及落在这些区间的数据的数量。

而返回的每条数据并不会是一个区间,而是一个开始的数据,也就是说下面的例子会返回的 key 是 20,25,30 等。

比方咱们想对 age 字段进行直方图聚合,步长为 5,用到的聚合的字段为 histogram,示例如下:

GET /bank/_search
{
  "size": 0,
  "aggs": {
    "age_histogram": {
      "histogram": {
        "field": "age",
        "interval": 5
      }
    }
  }
}

在 histogram 聚合字段下,field 字段为咱们要进行直方图聚合的字段,这里是 age 字段,interval 字段为进行划分的区间,咱们定义为 5。

返回的数据如下:

  ...
  "aggregations" : {
    "age_histogram" : {
      "buckets" : [
        {
          "key" : 20.0,
          "doc_count" : 225
        },
        {
          "key" : 25.0,
          "doc_count" : 226
        }
        ...
    ]
  }
}    

留神: 如果咱们进行聚合的区间,比如说 25-29 之间聚合的数据是 0,那么 es 还是会返回这个区间,不过 doc_count 是 0,不会存在不返回这个区间 key 的状况。

最小 count 返回数据

后面咱们说了就算区间 count 数是 0,这个区间也会返回,但同时咱们也能够规定 min_doc_count 这个参数来返回只有当区间 count 数大于等于这个值的时候才返回数据。

假如 age 的区间数据如下:

20-24:5

25-29:0

30-34:2

如果咱们设置 min_doc_count=2,那么返回的区间 25-29 则不会被返回,应用示例如下:

GET /bank/_search
{
  "size": 0,
  "aggs": {
    "age_histogram": {
      "histogram": {
        "field": "age",
        "interval": 5,
        "min_doc_count": 2
      }
    }
  }
}

返回数据:

  ...
  "aggregations" : {
    "age_histogram" : {
      "buckets" : [
        {
          "key" : 20.0,
          "doc_count" : 5
        },
        {
          "key" : 30.0,
          "doc_count" : 2
        },
        ...
     ]
   }
 }

指定返回区间

后面介绍的示例中,如果数据在 20-50 之间,那么返回的区间数据就从 20 开始计数(具体的 key 会依据 interval 的设置不一样,比方设置 Interval=5,key 就会是 20, 25, 30…,如果是设置 Interval=3,那么 key 就会是 18, 21, 24…)。

如果咱们想从 0 开始计数,即使是 0-20 之间的计数为 0,也想要返回 20 之前 0-4,5-9 的数,或者想要返回 50 之后的数据,包含 50-54,55-59 这种,咱们能够应用extended_bounds.minextended_bounds.max 来限定返回数据的最大最小值,示例如下:

GET /bank/_search
{
  "size": 0,
  "aggs": {
    "age_histogram": {
      "histogram": {
        "field": "age",
        "interval": 5,
        "extended_bounds": {
          "min": 0,
          "max": 90
        }
      }
    }
  }
}

这样返回的数据的区间就会在 0-90 之间,即使在全量数据的范畴之外。

留神: 因为在数据区间之外的数据为 0,想要扩大的区间返回显示,记得要将最小返回计数值 min_doc_count 置为 0。

6、嵌套聚合

嵌套聚合,这里针对的是 es 中数据字段为数组,数组元素里又嵌套为对象的状况,官网文档举了个例子,新建一个 products 的 index,数据结构如下:

PUT /products
{
    "mappings": {
        "properties" : {
            "resellers" : { 
                "type" : "nested",
                "properties" : {"reseller" : { "type" : "text"},
                    "price" : {"type" : "double"}
                }
            }
        }
    }
}

接下来咱们往里增加两条条数据:

PUT /products/_doc/0
{
  "name": "LED TV", 
  "resellers": [
    {
      "reseller": "companyA",
      "price": 350
    },
    {
      "reseller": "companyB",
      "price": 500
    }
  ]
}

PUT /products/_doc/1
{
  "name": "LED TV", 
  "resellers": [
    {
      "reseller": "companyA",
      "price": 400
    },
    {
      "reseller": "companyB",
      "price": 250
    }
  ]
}

而后咱们想要在这两条数据里的 resellers 数组字段里的四个元素里获取 price 字段最小值,能够通过 nested.path 来指定 resellers 字段,而后进行聚合,应用示例如下:

GET /products/_search
{
    "size": 0, 
    "query" : {"match" : { "name" : "led tv"}
    },
    "aggs" : {
        "resellers" : {
            "nested" : {"path" : "resellers"},
            "aggs" : {"min_price" : { "min" : { "field" : "resellers.price"} }
            }
        }
    }
}

7、范畴聚合

范畴聚合,即 range 聚合。咱们能够通过指定范畴来返回各个桶的数据,这个操作和直方图聚合是相似的,不过这个操作更灵便,聚合的范畴不会写死。

如果是心愿步长固定,咱们能够应用直方图聚合,比方 0 -4,5-9 这种,如果咱们间接想要自定义的 0-7,8-19 这种咱们想要定义的能够应用范畴聚合。

还是应用 age 字段来操作,比方咱们想要获取 小于 27,28-35,大于 36 这个范畴,咱们能够如下操作:

GET /bank/_search
{
  "size": 0,
  "aggs": {
    "age_range": {
      "range": {
        "field": "age",
        "ranges": [{"to": 27},
          {"from": 27, "to": 35},
          {"from": 35}
        ]
      }
    }
  }
}

须要留神的是,from 的参数是开区间的,比方咱们这里 from=27,那么逻辑就是 >27,如果区间两边没有限度,不填写相应的 from 和 to 参数即可,返回的 key 也会是 *-27 这种模式。

下面的命令返回的数据如下:r

  ...
  "aggregations" : {
    "age_range" : {
      "buckets" : [
        {
          "key" : "*-27.0",
          "to" : 27.0,
          "doc_count" : 326
        },
        {
          "key" : "27.0-35.0",
          "from" : 27.0,
          "to" : 35.0,
          "doc_count" : 384
        },
        {
          "key" : "35.0-*",
          "from" : 35.0,
          "doc_count" : 290
        }
      ]
    }
  }
}

如果想要返回的数据以 key:{} 的模式返回,能够加上 keyed=true 参数:

GET /bank/_search
{
  "size": 0,
  "aggs": {
    "age_range": {
      "range": {
        "field": "age",
        "keyed": true,
        "ranges": [{"to": 27},
          {"from": 27, "to": 35},
          {"from": 35}
        ]
      }
    }
  }
}

桶的子指标聚合

在下面的桶聚合操作之后,咱们还能够对每个桶进行子指标聚合,比如说最大最小值,平均值,或者统计值等,以下是个操作示例:

GET /bank/_search
{
  "size": 0,
  "aggs": {
    "age_range": {
      "range": {
        "field": "age",
        "ranges": [{"to": 27},
          {"from": 27, "to": 35},
          {"from": 35}
        ]
      },
      "aggs": {
        "age_stats": {
          "stats": {"field": "age"}
        }
      }
    }
  }
}

进行指标聚合的范畴是分到每个桶的数据。

8、罕见词聚合

rare terms aggregation,这个的概念大略是这样的,比方咱们依据 age 字段进行聚合,统计他们在文档中呈现的次数,咱们想要获取呈现次数起码的几个,或者指定呈现次数少于 50 的 age 值,就能够用到这个操作。

接下来咱们对 age 字段进行这样的操作,只获取呈现次数少于 50 的数据,示例如下:

GET /bank/_search
{
  "size": 0,
  "aggs": {
    "rare_age": {
      "rare_terms": {
        "field": "age",
        "max_doc_count": 50
      }
    }
  }
}

这个的关键字是 rare_terms,rare_age 是咱们指定的聚合名称,其下 field 是咱们进行聚合字段,在这里是 age 字段,max_doc_count 则是咱们指定的呈现次数最大的值。

返回的数据会依照 doc_count 正序排列返回,大抵如下:

  ...
  "aggregations" : {
    "rare_age" : {
      "buckets" : [
        {
          "key" : 29,
          "doc_count" : 35
        },
        {
          "key" : 27,
          "doc_count" : 39
        },
        {
          "key" : 38,
          "doc_count" : 39
        },
        ...

范畴过滤

咱们还能够应用过滤的形式来指定或者排除某些值,这个操作是反对正则的,但通过测试,发现依照官网文档应用正则的 * 来筛选数据并不能真正起作用,所以这里咱们介绍应用列表来实现过滤。

比方咱们指定的 age 范畴是 [29, 27, 24],应用 include:

GET /bank/_search
{
  "size": 0,
  "aggs": {
    "rare_age": {
      "rare_terms": {
        "field": "age",
        "max_doc_count": 51,
        "include": [29, 27, 24]
      }
    }
  }
}

如果咱们要排除的 age 范畴是 [29, 27, 24],应用 exclude:

GET /bank/_search
{
  "size": 0,
  "aggs": {
    "rare_age": {
      "rare_terms": {
        "field": "age",
        "max_doc_count": 51,
        "exclude": [29, 27, 24]
      }
    }
  }
}

9、矩阵聚合

矩阵聚合是很小的一部分,这里间接介绍一下。

后面在指标聚合的介绍中,有一个聚合统计汇总,其中介绍了一个参数是 stats,会返回对应字段的最大值、最小值、总数等数据,矩阵聚合 matrix 能够了解成是多个字段的 stats 的汇合,会短少一些统计值,然而返回的值更偏统计学方面的用处。

应用示例如下:

GET /bank/_search
{
  "size": 0,
  "aggs": {
    "field_statis": {
      "matrix_stats": {"fields": ["age", "balance"]
      }
    }
  }
}

返回的数据如下:

  ...
  "aggregations" : {
    "field_statis" : {
      "doc_count" : 1000,
      "fields" : [
        {
          "name" : "balance",
          "count" : 1000,
          "mean" : 25714.837000000014,
          "variance" : 1.9757153733576667E8,
          "skewness" : -0.009992486755643138,
          "kurtosis" : 1.8088323899074914,
          "covariance" : {
            "balance" : 1.9757153733576667E8,
            "age" : -2845.650777777781
          },
          "correlation" : {
            "balance" : 1.0,
            "age" : -0.033676422195874786
          }
        },
        {
          "name" : "age",
          "count" : 1000,
          ...
        }
    ...

其中,各参数的释义如下:

count: 总数

mean: 平均值

variance: 方差

skewness: 偏度

kurtosis: 峰度

covariance: 协方差

correlation: 与其余字段的相关性,比方 age 到 age 字段的相关性就是 1.0

如果想获取更多后端相干文章,可扫码关注浏览:

正文完
 0