所有内容皆可聚合:Elasticsearch 7 中的新聚合
自 1.0 版本以来,聚合框架一直是 Elasticsearch 的重要组成部分,多年来已进行多次优化、修复,甚至是一些大修。自从 Elasticsearch 7.0 发布以来,Elasticsearch 中已添加了相当多的新聚合,例如 rare_terms
、top_metrics
或 auto_date_histogram
聚合。在本篇博文中,我们将探究其中的一些聚合,仔细看看这些聚合能为您做些什么。
为了测试这些新的聚合,我们需要在 Elasticsearch 7.9 部署中设置一个示例数据集。要想确保自己拥有所有最新功能(以及修补程序),让集群保持最新版本是一个不错的方式;但如果您用的版本较旧,而且升级也不是可取的办法,则可以通过 Elastic Cloud 的免费试用版来实现这一目的。
下面的文档可代表一个电子商务用例,在这个用例中,用户点击一款产品并检索产品详细信息。当然,这当中缺少许多细节,例如单个用户的会话 ID。后续您可以启动和监测会话,甚至可以更进一步,利用转换来深入洞察您的数据。本篇博文力求简单明了,确保所有概念都能讲明白。
在 Kibana 中使用 Discover 或从命令行中使用 cURL 来索引示例数据:
PUT website-analytics/_bulk?refresh { "index" : {}} { "product_id":"123", "@timestamp" :"2020-10-01T11:11:23.000Z", "price" :12.34, "response_time_ms":242 } { "index" : {}} { "product_id":"456", "@timestamp" :"2020-10-02T12:14:00.000Z", "price" :20.58, "response_time_ms":98 } { "index" : {}} { "product_id":"789", "@timestamp" :"2020-10-03T13:15:00.000Z", "price" :34.16, "response_time_ms":123 } { "index" : {}} { "product_id":"123", "@timestamp" :"2020-10-02T14:16:00.000Z", "price" :12.34, "response_time_ms":465 } { "index" : {}} { "product_id":"123", "@timestamp" :"2020-10-02T14:18:00.000Z", "price" :12.34, "response_time_ms":158 } { "index" : {}} { "product_id":"123", "@timestamp" :"2020-10-03T15:17:00.000Z", "price" :12.34, "response_time_ms":168 } { "index" : {}} { "product_id":"789", "@timestamp" :"2020-10-06T15:17:00.000Z", "price" :34.16, "response_time_ms":220 } { "index" : {}} { "product_id":"789", "@timestamp" :"2020-10-10T15:17:00.000Z", "price" :34.16, "response_time_ms":99 }
Auto-bucketing 聚合
这些类型的聚合改变了定义存储桶的方式。当您在使用基于时间的聚合时,通常会根据时间间隔(例如 1d
)来定义存储桶。但有时您并不知道数据的性质,从用户的角度来看,只告诉预期的存储桶数会更容易一些。
这就是下面两个新聚合发挥作用的地方。
auto_date_histogram 聚合
auto_date_histogram
聚合在日期字段上运行,允许您配置期望返回的存储桶数。下面我们在小型数据集上尝试一下:
POST website-analytics/_search?size=0 { "aggs": { "views_over_time": { "auto_date_histogram": { "field": "@timestamp", "buckets":3 } } } } POST website-analytics/_search?size=0 { "aggs": { "views_over_time": { "auto_date_histogram": { "field": "@timestamp", "buckets":10 } } } }
通过运行这两个查询会看到,返回的 interval
是根据所请求的存储桶数确定的。如果请求的是 3 个存储桶,则应为每周 1 个存储桶,而如果请求的是 10 个存储桶,则应为每天 1 个存储桶。
如果需要最小时间间隔,也可以这样配置,详情可参见 auto_date_histogram 文档。
variable_width_histogram 聚合
通过可变宽度直方图,可按照预配置的数量动态创建存储桶。此外,与常规直方图聚合的固定宽度相比,这些存储桶的宽度是可变的。
POST website-analytics/_search?size=0 { "aggs": { "prices": { "variable_width_histogram": { "field": "price", "buckets":3 } } } }
由于我们的数据集中只有三种不同价格,因此最小值/最大值/键值都是相同的。但是,您可以尝试使用两个存储桶,就会看到一个存储桶现在具有不同的值。
另外,请记住,存储桶边界都是近似值。
在电子商务应用程序中可能就有这方面的用例,例如,您希望在分面导航中显示价格存储桶。但是,使用这个选项会让您的网站导航对异常值相当敏感,因此请在执行此操作之前最好设置一个类别筛选器。
感谢社区成员 James 将这个聚合引入 Elasticsearch,并对分层凝聚聚类算法进行了 Grok 解析。有关更多详情,请参阅 GitHub 拉取请求。
字符串聚合
以下聚合适用于基于字符串的字段,通常为 keyword
字段。
rare_terms 聚合
作为 Elastic Stack 用户,您可能或多或少对 terms 聚合已有所了解。这个聚合将返回数据集中出现次数最多的词。您也可以更改排序方式来返回出现次数最少的词。但是,这会产生一个无界误差,因此结果可能是一个近似值,因为这些数据是通过集群中的几个分片收集而来的。这是因为 Elasticsearch 会尝试阻止从不同分片将所有数据复制到一个节点,因为这样做既昂贵又缓慢。
rare_terms
聚合会尝试通过使用与 terms
聚合不同的实施过程来规避这些问题。尽管这仍是在进行一个近似计数,但 rare_terms
聚合具有一个定义明确的有界误差。
要找出在上述数据集中索引最少的产品 ID,请尝试以下操作
POST website-analytics/_search?size=0 { "aggs": { "rarest_product_ids": { "rare_terms": { "field": "product_id.keyword" } } } }
此外,您还可以使用 max_doc_count(与 terms
聚合的 min_doc_count
相反),并更改要返回的存储桶数。
string_stats 聚合
如何获取有关数据中字符串字段值的一些统计信息呢?下面让我们来试试 string_stats 聚合:
POST website-analytics/_search?size=0 { "aggs": { "rarest_product_ids": { "string_stats": { "field": "product_id.keyword", "show_distribution" : true } } } }
这将返回有关该字段中字符串的最小/最大/平均长度的统计信息,但是通过添加 show_distribution
参数,您还可看到所找到的每个字符的分布。
这是一种快速检查数据来发现异常值的简便方法,这些异常值可能是错误索引的数据,例如,产品 ID 过长或过短。此外,返回的香农熵值还可用于发现 DNS 数据渗透尝试之类的目的。
基于指标的聚合
接下来我们深入了解一下第二组聚合,即在分桶的数字字段上进行计算的聚合。
top_metrics 聚合
您可能对 top_hits 聚合已有所了解,这个聚合会返回完整的命中结果(包括命中源)。但是,如果您只想查看单个值,并希望以此排序,请查看 top_metrics 聚合。如果您不需要整个文档,那么这个聚合将比 top_hits
聚合快很多,通常用于从每个存储桶中检索最新值。
在我们的点击流数据集中,您可能会对最新点击事件的价格感兴趣。
POST website-analytics/_search { "size":0, "aggs": { "tm": { "top_metrics": { "metrics": {"field": "price"}, "sort": {"@timestamp": "desc"} } } } }
这里还支持按 _score
或地理距离排序。另外,您还可以指定多个指标,这样您可以向 metrics
字段添加另一个字段,然后需要将其变为数组。
boxplot 聚合
boxplot 聚合正如其名称所示,提供箱形图:
GET website-analytics/_search { "size":0, "aggs": { "by_date": { "date_histogram": { "field": "@timestamp", "calendar_interval": "day", "min_doc_count":1 }, "aggs": { "load_time_boxplot": { "boxplot": { "field": "price" } } } } } }
上面的查询会返回每天的箱形图,其中每日存储桶中存有数据。
我们将跳过 t-test 聚合,因为此处的超小型数据集无法执行任何有用的聚合请求。要查看这个聚合的值,您需要有一个数据集,在其中假设可以通过统计假设发现的行为变化。
管道聚合
接下来是在聚合之上的聚合,称为管道聚合。去年添加了很多这类聚合。
cumulative_cardinality 聚合
这个聚合对于查找数据集中新项目的数量非常有用。
GET website-analytics/_search { "size":0, "aggs": { "by_day": { "date_histogram": { "field": "@timestamp", "calendar_interval": "day" }, "aggs": { "distinct_products": { "cardinality": { "field": "product_id.keyword" } }, "total_new_products": { "cumulative_cardinality": { "buckets_path": "distinct_products" } } } } } }
通过上面的查询,您可以计算出每天有多少新产品和之前不知名产品被访问过,并创建这些产品的计数。在电子商务环境中,这可能有助于您查明新产品是否得到了真正关注,或者畅销产品是否位居榜首,如果不是,您也许应该改变一下营销方式了。
标准化聚合
下面我们大致了解一下,按百分比来计算,哪一天的流量最大,其中 100% 是指与查询对应的全部数据。
GET website-analytics/_search { "size":0, "aggs": { "by_day": { "date_histogram": { "field": "@timestamp", "calendar_interval": "day" }, "aggs": { "normalize": { "normalize": { "buckets_path": "_count", "method": "percent_of_sum" } } } } } }
这将返回每个存储桶的附加信息:每个存储桶中找到的文档数与搜索返回的总文档数的百分比。
您可能需要查看标准化聚合文档,因为可以从中选择更多的 method
值,例如,均值或某个范围内的重新定标。
moving percentiles 聚合
这个管道聚合在 percentiles 聚合之上运行,并使用滑动窗口计算累积百分位数。
GET website-analytics/_search { "size":0, "aggs": { "by_day": { "date_histogram": { "field": "@timestamp", "calendar_interval": "day" }, "aggs": { "response_time": { "percentiles": { "field": "response_time_ms", "percents": [ 75, 99 ] } }, "moving_pct": { "moving_percentiles": { "buckets_path": "response_time", "window":2 } } } } } }
这里需要展开讲一下,我们继续吧。在按天进行存储后,percentiles
聚合将计算每天存储桶的百分位数。然后,moving_percentiles
管道聚合取前两个存储桶并从中计算出移动平均值。请注意,如果您还想包含当前的存储桶,则可以通过使用 shift
参数来更改用于计算的存储桶的行为。
pipeline inference 聚合
我们将跳过 inference 存储桶聚合,因为计划会很快发布一篇博文来专门解释这个聚合。先满足一下大家的好奇心:您可以针对父级存储桶聚合的结果运行经过预训练的模型。敬请关注!
支持 histogram
字段类型
这不是一个严格意义上的聚合,但聚合会受到这种数据类型的影响,因此特意在此提一下。
您可能已经或多或少接触过 histogram 字段类型,它允许您存储预聚合的数值数据,例如,该数据在 Elastic 可观测性中就得到了广泛的使用。这一特殊字段类型支持聚合的子集,当然,您也会发现一些尚不受支持的聚合。为了支持这些聚合,还有大量工作需要去做。
在地理位置聚合中支持 geo_shape
同样,这也不是严格意义上的单个聚合,但这是一个巨大的进步,因此也有必要提一下。
除了 geo_point
字段类型之外,我们还投巨资以使 geo_bounds、geo_tile、geo_hashgrid 和 geo_centroid 聚合能与 geo_shape
字段类型一起使用。
更多聚合,敬请期待
感谢您的关注。如果您对这些聚合还有其他疑问,请在我们的讨论论坛中继续提问。此外,在即将发布的版本中,我们还会计划添加更多的聚合。其中一个就是 rate 聚合,它可以在 date_histogram
聚合中使用,并计算每个存储桶中文档或字段的比率。
如果您想尝试这些聚合,可在 Elastic Cloud 的免费试用版中快速部署一个集群,然后使用自己的数据自由操作!