欢迎访问shiker.tech

请允许在我们的网站上展示广告

您似乎使用了广告拦截器,请关闭广告拦截器。我们的网站依靠广告获取资金。

ES聚合查询,应该这么用!
(last modified Jun 17, 2024, 12:33 AM )
by
侧边栏壁纸
  • 累计撰写 192 篇文章
  • 累计创建 66 个标签
  • 累计收到 4 条评论

目 录CONTENT

文章目录

ES聚合查询,应该这么用!

橙序员
2024-06-16 / 0 评论 / 0 点赞 / 661 阅读 / 2,491 字 / 正在检测百度是否收录... 正在检测必应是否收录...
文章摘要(AI生成)

Elasticsearch的聚合功能分为三种类型:指标聚合、存储桶聚合和管道聚合。存储桶聚合将文档分组到存储桶中,类似于mysql中的group by字段;指标聚合计算指标值,类似于mysql中的聚合函数;管道聚合则对其他聚合的输出进行操作。存储桶聚合创建文档存储桶,并确定文档是否属于该存储桶;指标聚合根据文档字段的值计算指标值;管道聚合将信息添加到输出树中。管道聚合可以分为父聚合和同级聚合,分别提供其父聚合或同级聚合的输出。总的来说,Elasticsearch的聚合功能可以根据不同的需求灵活使用,对数据进行更深层次的分析和统计。

三种聚合简介

Elasticsearch 将聚合分为三类:

  • 根据字段值计算指标(如总和或平均值)的指标聚合。
  • 根据字段值、范围或其他条件将文档分组到存储桶(也称为箱)中的存储桶聚合。
  • 从其他聚合(而不是文档或字段)获取输入的管道聚合。

如果将三种聚合与mysql做对比,存储桶聚合的作用相当于group by字段;而指标聚合则相当于mysql中的各种聚合函数(maxminavgsum);而管道聚合则是相当与针对聚合子查询的二次操作

存储桶聚合

存储桶聚合不会像指标聚合那样计算字段的指标,而是创建文档存储桶。每个存储桶都与一个标准(取决于聚合类型)相关联,该标准确定当前上下文中的文档是否“落入”其中。换句话说,桶有效地定义了文档集。除了存储桶本身之外, bucket 聚合还计算并返回“落入”每个存储桶的文档数量。

metrics 聚合相反,桶聚合可以保存子聚合。这些子聚合将针对由其“父”存储桶聚合创建的存储桶进行聚合。

有不同的存储桶聚合器,每个存储桶聚合器都有不同的“存储桶”策略。有些定义单个存储桶,有些定义固定数量的多个存储桶,还有一些在聚合过程中动态创建存储桶。

指标聚合

该系列中的聚合基于以一种或另一种方式从正在聚合的文档中提取的值来计算指标。这些值通常从文档的字段中提取(使用字段数据),但也可以使用脚本生成。

数字指标聚合是一种特殊类型的指标聚合,它输出数值。一些聚合输出单个数字指标(例如 avg )并称为 single-value numeric metrics aggregation ,其他聚合生成多个指标(例如 stats )并称为 multi-value numeric metrics aggregation

管道聚合

管道聚合作用于其他聚合(而不是文档集)生成的输出,从而将信息添加到输出树中。有许多不同类型的管道聚合,每种类型计算来自其他聚合的不同信息,但这些类型可以分为两类:

  • 父聚合

一系列管道聚合,提供其父聚合的输出,并且能够计算新存储桶或新聚合以添加到现有存储桶。

  • 同级聚合

管道聚合提供同级聚合的输出,并且能够计算与同级聚合处于同一级别的新聚合。

管道聚合可以通过使用 buckets_path 参数来引用执行计算所需的聚合,以指示所需指标的路径。

管道聚合不能有子聚合,但根据类型,它可以引用 buckets_path 中的另一个管道,从而允许链接管道聚合。例如,您可以将两个导数链接在一起来计算二阶导数(即导数的导数)。

聚合查询的初级使用

桶+指标聚合

例如我们要计算所有销售的平均价格以及所有 T 恤销售的平均价格。

POST /sales/_search?size=0&filter_path=aggregations
{
  "aggs": {
    "avg_price": { "avg": { "field": "price" } },
    "t_shirts": {
      "filter": { "term": { "type": "t-shirt" } },
      "aggs": {
        "avg_price": { "avg": { "field": "price" } }
      }
    }
  }
}

image-20240616154517233

则返回:

{
  "aggregations": {
    "avg_price": { "value": 140.71428571428572 },
    "t_shirts": {
      "doc_count": 3,
      "avg_price": { "value": 128.33333333333334 }
    }
  }
}

管道聚合

我们可以使用平均桶 avg_bucket 计算每月平均销售额:

POST _search
{
  "size": 0,
  "aggs": {
    "sales_per_month": {
      "date_histogram": {
        "field": "date",
        "calendar_interval": "month"
      },
      "aggs": {
        "sales": {
          "sum": {
            "field": "price"
          }
        }
      }
    },
    "avg_monthly_sales": {           
      "avg_bucket": {
        "buckets_path": "sales_per_month>sales",
        "gap_policy": "skip",
        "format": "#,##0.00;(#,##0.00)"
      }      
    }
  }
}

image-20240616154324054

聚合查询的进阶使用

小心邻接矩阵

在使用存储桶聚合时,需要注意的是过滤条件最好不要有多个,这样会产生邻接矩阵。即使用A和B过滤条件查询,会产生A、A&B、B三个返回结果。

image-20240616152155010

对于 N 个过滤器,生成的桶矩阵可能是 N²/2,这可能会很昂贵。断路器设置可防止结果产生过多的存储桶,并避免过多的磁盘寻道, indices.query.bool.max_clause_count 设置用于限制过滤器的数量。

多字段(术语)聚合

假如我们有一个场景,需要对根据索引的A、B、C三个字段进行分组,并统计每组的数量。那么我们有以下三种方案:

嵌套子聚合

  • 场景:需要对多个字段逐层进行细化分析。

  • 示例:首先按A字段分组,再在每个A组内按B字段分组,最后在每个B组内按C字段分组。

    image-20240616162834758

多术语聚合(multi-terms aggregation):

  • 场景:需要同时对多个字段进行组合聚合。

  • 示例:按A、B、C字段组合分组,而不是逐层分组。

    image-20240616162921766

复合聚合(composite aggregation):

  • 场景:需要分页处理大量聚合结果。
  • 示例:在按A、B、C字段组合分组,然后进行分组统计并分页获取结果。

这三种方式都可以实现多条件分组聚合,但是这其中由于嵌套子查询为层层聚合,所以时间复杂度要比后两者聚合更高。而多术语聚合一般用于获取topN结果,只有复合查询能获取到全部结果。ES官方文档对这两者的描述为:

The multi_term aggregations are the most useful when you need to sort by a number of document or a metric aggregation on a composite key and get top N results. If sorting is not required and all values are expected to be retrieved using nested terms aggregation or composite aggregations will be a faster and more memory efficient solution.

您需要按多个文档或复合键上的度量聚合进行排序并获取前 N 个结果时,multi_term 聚合最有用。如果不需要排序,并且希望使用来检索所有值,嵌套术语聚合或者 composite aggregations 将是更快、内存效率更高的解决方案。

桶过滤条件

要限制搜索中运行所有聚合的文档,请使用顶级 query 。这比带有子聚合的单个 filter 聚合更快。

例如使用:

{
  "query": { "term": { "type": "t-shirt" } },
  "aggs": {
    "avg_price": { "avg": { "field": "price" } }
  }
}

而不是:

{
  "aggs": {
    "t_shirts": {
      "filter": { "term": { "type": "t-shirt" } },
      "aggs": {
        "avg_price": { "avg": { "field": "price" } }
      }
    }
  }
}

要使用多个过滤器对文档进行分组,请使用 filters 聚合。这比多个 filter 聚合更快。

例如使用:

{
  "aggs": {
    "f": {
      "filters": {
        "filters": {
          "hats": { "term": { "type": "hat" } },
          "t_shirts": { "term": { "type": "t-shirt" } }
        }
      },
      "aggs": {
        "avg_price": { "avg": { "field": "price" } }
      }
    }
  }
}

而不是

{
  "aggs": {
    "hats": {
      "filter": { "term": { "type": "hat" } },
      "aggs": {
        "avg_price": { "avg": { "field": "price" } }
      }
    },
    "t_shirts": {
      "filter": { "term": { "type": "t-shirt" } },
      "aggs": {
        "avg_price": { "avg": { "field": "price" } }
      }
    }
  }
}

索引相关聚合参数

search.max_buckets :ES集群设置限制单个响应中允许的存储桶数量。

sum_other_doc_count :未进入顶级 size 术语的文档数量。如果这大于 0 ,您可以确定 terms agg 必须丢弃一些存储桶,因为它们 size 不适合协调节点,或者 shard_size 不适合数据节点。

show_term_doc_count_error :如果将 show_term_doc_count_error 参数设置为 trueterms 则聚合将包括 doc_count_error_upper_bound ,这是每个分片 doc_count 返回的误差的上限。它是每个分片上不适合的最大存储桶大小的总和 shard_size

查询参数设置

order参数:您可以使用 order 参数指定不同的排序顺序,但ES不建议这样做。创建一个只会返回*错误结果*的术语排序非常容易,并且很难排查出来。请谨慎更改此设置。尤其避免使用 "order": { "_count": "asc" } .如果您需要查找稀有术语,请改用 rare_terms 聚合。由于terms 聚合从分片中获取术语的方式 ,按文档计数升序排序通常会产生不准确的结果。

size 参数:默认情况下, terms 聚合返回文档最多的前十个术语。使用该 size 参数可返回更多术语,最高可达 search.max_buckets 限制。

0

评论区