聚合 API
聚合将您的数据汇总为 buckets 或 metrics 的统计信息。
聚合可以回答以下问题:
- 所有售出商品的平均价格是多少?
- 我们每天有多少个状态码为 500 的错误?
- 按颜色分组的汽车平均售价是多少?
前提条件
要在字段上使用聚合,需要为此字段创建快速字段索引。 快速字段索引是一种列式存储,其中文档的值会被提取并存储。
例如,为文本创建一个快速字段以进行术语聚合。
name: category
type: text
tokenizer: raw
record: basic
fast: true
有关更多详细信息和示例,请参阅 索引配置 页面。
API 终端
聚合的终端是搜索终端:
- Quickwit API:
api/v1/<index id>/search
- Elasticsearch API:
api/v1/_elastic/<index_id>/_search
格式
聚合请求和结果将以与Elasticsearch兼容的JSON格式进行序列化和反序列化。 除非另有说明,否则您应该能够直接使用您的Elasticsearch聚合查询。
在下面的一些示例中,显示的不是完整的请求,而是仅显示 aggregations
的有效负载。
示例
Request
{
"query": "*",
"max_hits": 0,
"aggs": {
"sites_and_aqi": {
"terms": {
"field": "County",
"size": 2,
"order": { "average_aqi": "asc" }
},
"aggs": {
"average_aqi": {
"avg": { "field": "AQI" }
}
}
}
}
}
Response
...
"aggregations": {
"sites_and_aqi": {
"buckets": [
{
"average_aqi": {
"value": 32.62267569707098
},
"doc_count": 56845,
"key": "臺東縣"
},
{
"average_aqi": {
"value": 35.97893635571055
},
"doc_count": 28675,
"key": "花蓮縣"
}
],
"sum_other_doc_count": 1872055
}
}
支持的聚合
- Bucket
- Metric
Bucket 聚合
桶聚合(BucketAggregations)将文档划分为桶。每个桶都关联了一个规则,该规则确定文档是否落入该桶。 换句话说,这些桶实际上定义了文档集。桶不一定是互斥的,因此一个文档可以落入多个桶。 除了桶本身之外,桶聚合还会计算并返回每个桶中的文档数量。 与度量聚合不同,桶聚合可以包含子聚合。 这些子聚合将为其“父”桶聚合创建的桶进行聚合。 有不同的桶聚合器,每种都有不同的“分桶”策略。 有些定义单个桶,有些定义固定数量的多个桶,还有些在聚合过程中动态创建桶。
示例请求,每个桶中的直方图和统计数据:
对 datetime 字段进行聚合
有关 datetime
字段更方便的 API,请参阅 DateHistogram
。
类型为 datetime
的字段处理方式与任何数值字段相同。但是,请求中的所有值(如间隔、偏移、边界和范围边界)都需要用毫秒表示。
对 datetime
字段按天划分的直方图。interval
需要以毫秒为单位提供。
在以下示例中,我们将文档按天分组(1 天 = 86400000 毫秒
)。
返回的格式目前固定为 Rfc3339
。
Request
{
"query": "*",
"max_hits": 0,
"aggs": {
"datetime_histogram":{
"histogram":{
"field": "datetime",
"interval": 86400000
}
}
}
}
Response
{
...
"aggregations": {
"datetime_histogram": {
"buckets": [
{
"doc_count": 1,
"key": 1546300800000000.0,
"key_as_string": "2019-01-01T00:00:00Z"
},
{
"doc_count": 2,
"key": 1546560000000000.0,
"key_as_string": "2019-01-04T00:00:00Z"
}
]
}
}
}
直方图
直方图是一种桶聚合,其中桶根据给定的间隔动态创建。每个文档值向下舍入到其对应的桶。
例如,如果我们有一个价格 18 和一个间隔 5,文档将落入键为 15 的桶。使用的公式为:((val - offset) / interval).floor() * interval + offset。
返回的桶
默认情况下,返回的桶介于文档的最小值和最大值之间,包括空桶。将 min_doc_count 设置为 != 0 将过滤掉空桶。
桶的值范围可以通过 extended_bounds 扩展或通过 hard_bounds 限制范围。
示例
{
"query": "*",
"max_hits": 0,
"aggs": {
"prices": {
"histogram": {
"field": "price",
"interval": 10
}
}
}
}
参数
field
要进行聚合的字段。
目前,此聚合仅适用于类型为 u64
、f64
、i64
和 datetime
的快速字段。
keyed
将响应格式从数组更改为哈希映射,桶中的 key
将成为哈希映射中的 key
。
interval
用于分段数据范围的间隔。每个桶覆盖的值范围为 [0..interval)。必须大于 0。
offset
间隔隐式定义了一个绝对的桶网格 [interval * k, interval * (k + 1))
。
偏移量使网格平移成为可能,即 [offset + interval * k, offset + interval * (k + 1))
。偏移量必须在范围 [0, interval) 内。
例如,如果有两个值分别为 8 和 12 的文档且间隔为 10.0,则它们将落入键为 0 和 10 的桶。如果偏移量为 5 且间隔为 10,则它们都将落入键为 5 且范围为 [5..15) 的桶。
{
"query": "*",
"max_hits": 0,
"aggs": {
"prices": {
"histogram": {
"field": "price",
"interval": 10,
"offset": 2.5
}
}
}
}
min_doc_count
返回的桶中的最小文档数量。默认值为 0。
hard_bounds
将数据范围限制在闭区间 [min, max]。
如果值不在数据范围内,可以用来过滤这些值。
hard_bounds
仅限制桶,若要强制设定范围,请将 extended_bounds
和 hard_bounds
同时设置为相同的范围。
{
"query": "*",
"max_hits": 0,
"aggs": {
"prices": {
"histogram": {
"field": "price",
"interval": 10,
"hard_bounds": {
"min": 0,
"max": 100
}
}
}
}
}
extended_bounds
可以设置以扩展您的边界。桶的范围默认由文档值的数据范围定义。顾名思义,这只能用于扩展值范围。如果 min 或 max 的边界没有扩展范围,则该值对返回的桶没有影响。
不能与 min_doc_count
> 0 一起设置,因为扩展边界产生的空桶不会被返回。
{
"query": "*",
"max_hits": 0,
"aggs": {
"prices": {
"histogram": {
"field": "price",
"interval": 10,
"extended_bounds": {
"min": 0,
"max": 100
}
}
}
}
}
日期直方图
DateHistogram
与 Histogram
类似,但它只能用于 datetime 类型,并且提供了更方便的 API 来定义间隔。
与直方图类似,值会被向下舍入到最近的桶。
返回的格式目前固定为 Rfc3339
。
限制
仅支持通过 fixed_interval
参数设置的固定时间间隔。
不支持 interval
和 calendar_interval
参数。
Request
{
"query": "*",
"max_hits": 0,
"aggs": {
"sales_over_time": {
"date_histogram": {
"field": "sold_at",
"fixed_interval": "30d"
"offset": "-4d"
}
}
}
}
Response
{
...
"aggregations": {
"sales_over_time" : {
"buckets" : [{
"key_as_string" : "2015-01-01T00:00:00Z",
"key" : 1420070400000,
"doc_count" : 4
}]
}
}
}
参数
field
要进行聚合的字段。
目前,此聚合仅适用于类型为 datetime
的快速字段。
keyed
将响应格式从数组更改为哈希映射,桶中的 key
将成为哈希映射中的 key
。
interval
用于分段数据范围的间隔。每个桶覆盖的值范围为 [0..interval)。必须大于 0。
固定间隔通过 fixed_interval
参数配置。
固定间隔是指固定数量的国际单位制单位,并且无论落在日历上的哪个位置都不会偏离。一秒始终由 1000 毫秒组成。
这允许固定间隔以支持单位的任意倍数指定。然而,这意味着固定间隔无法表达其他单位,如月份,因为一个月的持续时间不是一个固定的量。
尝试指定像月份或季度这样的日历间隔将返回错误。
固定间隔接受的单位如下:
ms
:毫秒s
:秒。定义为每个 1000 毫秒。m
:分钟。定义为每个 60 秒(60_000 毫秒)。h
:小时。定义为每个 60 分钟(3_600_000 毫秒)。d
:天。定义为 24 小时(86_400_000 毫秒)。
不支持分数时间值,但这可以通过转换到另一个时间单位来解决(例如,1.5h
可以指定为 90m
)。
offset
间隔隐式定义了一个绝对的桶网格 [interval * k, interval * (k + 1))
。
偏移量使网格平移成为可能,即 [offset + interval * k, offset + interval * (k + 1))
。偏移量必须在范围 [0, interval) 内。
当使用 fixed_interval
时,这尤其有用,例如将第一个桶平移到年初。
offset
参数具有与 fixed_interval
参数相同的语法,但也允许负值。
min_doc_count
返回的桶中的最小文档数量。默认值为 0。
hard_bounds
与 Histogram
中相同,但 min
和 max
参数需要设置为具有毫秒精度的时间戳。
extended_bounds
与 Histogram
中相同,但 min
和 max
参数需要设置为具有毫秒精度的时间戳。
Range
提供用户自定义的桶进行聚合。将自动创建两个特殊桶以覆盖整个值范围。
提供的桶必须是连续的。在聚合过程中,从 fast_field
字段提取的值将与每个桶范围进行比较。
注意,此聚合对于每个范围包括 from 值并排除 to 值。
限制/兼容性
不支持重叠范围。
Request
{
"query": "*",
"max_hits": 0,
"aggs": {
"my_scores": {
"range": {
"field": "score",
"ranges": [
{ "to": 3.0, "key": "low" },
{ "from": 3.0, "to": 7.0, "key": "medium-low" },
{ "from": 7.0, "to": 20.0, "key": "medium-high" },
{ "from": 20.0, "key": "high" }
]
}
}
}
}
Response
{
...
"aggregations": {
"my_scores" : {
"buckets": [
{"key": "low", "doc_count": 0, "to": 3.0},
{"key": "medium-low", "doc_count": 10, "from": 3.0, "to": 7.0},
{"key": "medium-high", "doc_count": 10, "from": 7.0, "to": 20.0},
{"key": "high", "doc_count": 80, "from": 20.0}
]
}
}
}
参数
keyed
将响应格式从数组更改为哈希映射,序列化的范围将是哈希映射中的 key
。
如果提供了自定义 key
,将使用自定义 key
。
field
要进行聚合的字段。
目前,此聚合仅适用于类型为 u64
、f64
、i64
和 datetime
的快速字段。
ranges
包含 from
和 to
值的桶列表。
from
值在范围内是包含的。
to
值在范围内是不包含的。
key
是可选的,并将在响应中用作桶的键。
第一个桶可以省略 from
值,最后一个桶可以省略 to
值。
注意,此聚合对于每个范围包含 from
值并排除 to
值。必要时,将创建额外的桶直到第一个 to
和最后一个 from
。
Terms
为每个唯一项创建一个桶并计算出现次数。
Request
{
"query": "*",
"max_hits": 0,
"aggs": {
"genres": {
"terms": { "field": "genre" }
}
}
}
Response
...
"aggregations": {
"genres": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{ "key": "drumnbass", "doc_count": 6 },
{ "key": "raggae", "doc_count": 4 },
{ "key": "jazz", "doc_count": 2 }
]
}
}
文档计数误差
在 Quickwit 中,每个分片有一个段。因此,从分片返回的结果相当于从段返回的结果。
为了提高性能,来自一个分片的结果会在 shard_size
处截断。
当合并多个分片的结果时,未进入分片结果前 n 名的术语会增加理论上的上限误差,具体数值为最低的术语计数。
即使设置了较大的 shard_size
值,术语聚合的 doc_count
值也可能是近似的。因此,术语聚合上的任何子聚合也可能近似。
sum_other_doc_count
是未进入前 size 名术语的文档数量。如果这个值大于 0,则可以确定术语聚合不得不丢弃一些桶,原因可能是它们在根节点上不符合 size
要求,或者在叶节点上不符合 shard_size
要求。
每个桶的文档计数误差
如果您将 show_term_doc_count_error
参数设置为 true,则术语聚合将包括 doc_count_error_upper_bound
,这是每个分片返回的 doc_count
误差的上限。
它是所有未进入 shard_size
的分片中最大桶大小的总和。
参数
field
要进行聚合的字段。
目前,术语聚合仅适用于类型为 text
、f64
、i64
和 u64
的快速字段。
size
默认情况下,返回文档数量最多的前 10 个术语。更大的 size
值会更耗费资源。
shard_size
为了获得更准确的结果,我们从每个段/分片获取超过 size
的术语。
增加这个值会提高准确性,但也会增加 CPU/内存使用。有关 shard_size
如何影响准确性的更多信息,请参见 文档计数误差 部分。
shard_size
表示从一个分片返回的术语数量。
例如,如果有 100 个分片,并且 shard_size
设置为 1000,则根节点可能会收到多达 100,000 个术语进行合并。
假设每个术语平均消耗 50 字节,这将需要最多 5MB 的内存。
实际发送到根节点的术语数量取决于一个节点处理的分片数量以及中间结果如何合并(例如,术语的基数)。
Quickwit 与 Elasticsearch 的区别说明:
- 不同于 Elasticsearch,Quickwit 不使用全局序号,因此序列化的术语需要发送到根节点。
- Elasticsearch 中的分片概念与 Quickwit 中的分片不同。在 Elasticsearch 中,一个分片包含多达 200M 份文档,并且是一组段的集合。相比之下,一个 Quickwit 分片包含一个段,通常有 5M 份文档。因此,在 Elasticsearch 中,
shard_size
适用于一组段,而在 Quickwit 中,它适用于单个段。
默认值为 size * 10
。
show_term_doc_count_error
如果您将 show_term_doc_count_error
参数设置为 true,则术语聚合将包括 doc_count_error_upper_bound
,这是每个分片返回的 doc_count
误差的上限。
它是所有未进入 shard_size
的分片中最大桶大小的总和。
当按计数降序排序时,默认值为 true。
min_doc_count
过滤掉所有低于 min_doc_count
的术语。默认值为 1。
昂贵:当设置为 0 时,这将返回字段中的所有术语。
order
设置排序顺序。这里的字符串是一个目标,可以是 _count
、_key
或度量子聚合的名称。
单值度量(如平均值)可以通过其名称引用。多值度量(如统计值)需要通过名称引用其字段,例如 stats.avg
。
限制:目前仅支持按一个属性排序。传递 order
数组不受支持,例如 "order": [{ "average_price": "asc" }, { "_key": "asc" }]
。
Order alphabetically
{
"query": "*",
"max_hits": 0,
"aggs": {
"genres": {
"terms": {
"field": "genre",
"order": { "_key": "asc" }
}
}
}
}
Order by sub_aggregation
{
"query": "*",
"max_hits": 0,
"aggs": {
"articles_by_price": {
"terms": {
"field": "article_name",
"order": { "average_price": "asc" }
},
"aggs": {
"average_price": {
"avg": { "field": "price" }
}
}
}
}
}
Metric 聚合
此类聚合基于从被聚合的文档中提取的值来计算度量。 值是从文档的快速字段中提取的。某些聚合输出单个数值度量(例如平均值),称为单值数值度量聚合;其他生成多个度量(例如统计值),称为多值数值度量聚合。
与桶聚合不同,度量不允许子聚合,因为没有文档集可供聚合。
Average
这是一种单值度量聚合,用于计算从聚合文档中提取的数值的平均值。
支持的字段类型为 u64
、f64
、i64
和 datetime
。
Request
{
"query": "*",
"max_hits": 0,
"aggs": {
"average_price": {
"avg": { "field": "price" }
}
}
}
Response
{
"num_hits": 9582098,
"hits": [],
"elapsed_time_micros": 101942,
"errors": [],
"aggregations": {
"average_price": {
"value": 133.7
}
}
}
Count
这是一种单值 metric 聚合,用于计算从聚合文档中提取的值的数量。
支持的字段类型为 u64
、f64
、i64
和 datetime
。
Request
{
"query": "*",
"max_hits": 0,
"aggs": {
"price_count": {
"value_count": { "field": "price" }
}
}
}
Response
{
"num_hits": 9582098,
"hits": [],
"elapsed_time_micros": 102956,
"errors": [],
"aggregations": {
"price_count": {
"value": 9582098
}
}
}
Max
这是一种单值 metric 聚合,用于计算从聚合文档中提取的数值的最大值。
支持的字段类型为 u64
、f64
、i64
和 datetime
。
Request
{
"query": "*",
"max_hits": 0,
"aggs": {
"max_price": {
"max": { "field": "price" }
}
}
}
Response
{
"num_hits": 9582098,
"hits": [],
"elapsed_time_micros": 101543,
"errors": [],
"aggregations": {
"max_price": {
"value": 1353.23
}
}
}
Min
这是一种单值 metric 聚合,用于计算从聚合文档中提取的数值的最小值。
支持的字段类型为 u64
、f64
、i64
和 datetime
。
Request
{
"query": "*",
"max_hits": 0,
"aggs": {
"min_price": {
"min": { "field": "price" }
}
}
}
Response
{
"num_hits": 9582098,
"hits": [],
"elapsed_time_micros": 102342,
"errors": [],
"aggregations": {
"min_price": {
"value": 0.01
}
}
}
Stats
一个多值指标聚合,用于计算从聚合文档中提取的数值(平均值、计数、最小值、最大值、标准差和总和)的统计信息。
支持的字段类型为 u64
、f64
、i64
和 datetime
。
Request
{
"query": "*",
"max_hits": 0,
"aggs": {
"timestamp_stats": {
"stats": { "field": "timestamp" }
}
}
}
Response
{
"num_hits": 10000783,
"hits": [],
"elapsed_time_micros": 65297,
"errors": [],
"aggregations": {
"timestamp_stats": {
"avg": 1462320207.9803998,
"count": 10000783,
"max": 1475669670.0,
"min": 1440670432.0,
"standard_deviation": 11867304.28681695,
"sum": 1.4624347076526848e16
}
}
}
Extended Stats
扩展统计(Extended stats)与 stats
相同,但增加了以下附加指标:sum_of_squares
、variance
、std_deviation
和 std_deviation_bounds
。
支持的字段类型为 u64
、f64
、i64
和 datetime
。
Request
{
"query": "*",
"max_hits": 0,
"aggs": {
"response_extended_stats": {
"extended_stats": { "field": "response" }
}
}
}
Response
{
..
"aggregations": {
"response_extended_stats": {
"avg": 65.55555555555556,
"count": 9,
"max": 130.0,
"min": 20.0,
"std_deviation": 42.97573245736381,
"std_deviation_bounds": {
"lower": -20.395909359172062,
"lower_population": -20.395909359172062,
"lower_sampling": -25.60973998562673,
"upper": 151.50702047028318,
"upper_population": 151.50702047028318,
"upper_sampling": 156.72085109673785
},
"std_deviation_population": 42.97573245736381,
"std_deviation_sampling": 45.582647770591144,
"sum": 590.0,
"sum_of_squares": 55300.0,
"variance": 1846.9135802469136,
"variance_population": 1846.9135802469136,
"variance_sampling": 2077.777777777778
}
}
}
Sum
一个单值指标聚合,用于汇总从聚合文档中提取的数值。
支持的字段类型为 u64
、f64
、i64
和 datetime
。
Request
{
"query": "*",
"max_hits": 0,
"aggs": {
"total_price": {
"sum": { "field": "price" }
}
}
}
Response
{
"num_hits": 9582098,
"hits": [],
"elapsed_time_micros": 101142,
"errors": [],
"aggregations": {
"total_price": {
"value": 12966782476.54
}
}
}
Percentiles
百分位聚合是一种有用的工具,用于了解数据集的分布情况。 它计算出低于某个给定百分比的数据值。 例如,第95百分位表示低于该值的数据点占95%。
这种聚合对于分析网站或服务响应时间特别有用。 例如,如果第95百分位的网站加载时间显著高于中位数,这表明一小部分用户的加载时间远慢于大多数用户。
要使用百分位聚合,你需要提供一个聚合字段。 在网站加载时间的情况下,这通常是一个包含站点加载所需时间的字段。
Request
{
"query": "*",
"max_hits": 0,
"aggs": {
"loading_times": {
"percentiles": {
"field": "load_time"
"percents": [90, 95, 99]
}
}
}
}
Response
{
"num_hits": 9582098,
"hits": [],
"elapsed_time_micros": 101142,
"errors": [],
"aggregations": {
"loading_times": {
"values": {
"90.0": 33.4,
"95.0": 83.4,
"99.0": 230.3
}
}
}
}
percents
可以省略,它将默认为 [1, 5, 25, 50 (中位数), 75, 95, 99]
。
估算百分位数
虽然百分位数提供了有关数据分布的宝贵见解,但重要的是要理解它们通常是估计值。 这是因为对于大型数据集计算精确的百分位数可能会耗费大量的计算资源和时间。
唯一基数
唯一基数聚合用于近似计算字段中的不同值的数量。 在处理大型数据集时,计算确切的不同值数量会非常耗费计算资源,此时唯一基数聚合是必不可少的。
唯一基数聚合可以用来计算访问网站的唯一用户数量,或者确定在特定时间段内登录服务器的唯一IP地址数量。
唯一基数聚合背后的算法基于HyperLogLog++,它提供了对哈希值的近似计数。
要使用唯一基数聚合,你需要指定要进行聚合的字段。
Request
{
"query": "*",
"max_hits": 0,
"aggs": {
"unique_users": {
"cardinality": {
"field": "user_id"
}
}
}
}
Response
{
"num_hits": 9582098,
"hits": [],
"elapsed_time_micros": 101142,
"errors": [],
"aggregations": {
"unique_users": {
"value": 345672
}
}
}
性能
对于具有大量唯一值的数据集,文本字段上的唯一基数聚合计算成本较高。 这是因为聚合需要为字段中的每个唯一术语计算哈希值。 为了实现这一点,Quickwit 首先需要收集每个分片的术语ID,然后从字典中获取这些术语ID对应的压缩术语。 解压缩术语相对较为昂贵,并且保留术语ID会增加内存使用量。
对于数值字段,唯一基数聚合更加高效,因为它直接计算数值的哈希值并将它们添加到 HLL++ 中。
限制
目前参数 precision_threshold
被忽略。通常情况下,它可以设置直到聚合结果精确的阈值。