elasticsearch - 对完整的潜在存储桶集执行管道聚合
问题描述
使用_search
Elasticsearch 的 API 时,如果您将 size 设置为 10,并执行avg
度量聚合,则平均值将是整个数据集与查询匹配的所有值,而不仅仅是hits
数组中返回的 10 个项目的平均值。
另一方面,如果您执行terms
聚合并将聚合的 设置size
为10,则对这些存储桶terms
执行聚合将仅计算这 10 个存储桶的平均值,而不是所有潜在存储桶。avg_buckets
terms
如何计算所有潜在存储桶中某个字段的平均值,但buckets
数组中仍然只有 10 个项目?
为了使我的问题更具体,考虑这个例子:假设我是一个帽子制造商。多家商店携带我的帽子。我有一个 Elasticsearch 索引hat-sales
,每次售出我的一顶帽子时都有一个文档。本文件中包括价格和出售帽子的商店。
以下是我测试过的文档的两个示例:
{
"type": "top",
"color": "black",
"price": 19,
"store": "Macy's"
}
{
"type": "fez",
"color": "red",
"price": 94,
"store": "Walmart"
}
如果我想找到我售出的所有帽子的平均价格,我可以运行这个:
GET hat-sales/_search
{
"size": 0,
"query": {
"match_all": {}
},
"aggs": {
"average_hat_price": {
"avg": {
"field": "price"
}
}
}
}
无论设置为 0、3 还是其他任何值,都average_hat_price
将是相同的。size
好的,现在我想找到销售帽子数量最多的前 3 家商店。我还想将它们与商店出售的平均帽子数量进行比较。所以我想做这样的事情:
GET hat-sales/_search
{
"size": 0,
"query": {
"match_all": {}
},
"aggs": {
"by_store": {
"terms": {
"field": "store.keyword",
"size": 3
},
"aggs": {
"sales_count": {
"cardinality": {
"field": "_id"
}
}
}
},
"avg sales at a store": {
"avg_bucket": {
"buckets_path": "by_store>sales_count"
}
}
}
}
这会产生一个响应
"aggregations" : {
"by_store" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 8,
"buckets" : [
{
"key" : "Macy's",
"doc_count" : 6,
"sales_count" : {
"value" : 6
}
},
{
"key" : "Walmart",
"doc_count" : 5,
"sales_count" : {
"value" : 5
}
},
{
"key" : "Dillard's",
"doc_count" : 3,
"sales_count" : {
"value" : 3
}
}
]
},
"avg sales at a store" : {
"value" : 4.666666666666667
}
}
问题是avg sales at a store
仅针对梅西百货、沃尔玛和迪拉德百货进行计算。如果我想找到所有商店的平均值,我必须设置aggs.by_store.terms.size
为 65536。(65536 因为这是默认的最大术语桶数,我不知道先验可能有多少桶。)这给出了结果:
"aggregations" : {
"by_store" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "Macy's",
"doc_count" : 6,
"sales_count" : {
"value" : 6
}
},
{
"key" : "Walmart",
"doc_count" : 5,
"sales_count" : {
"value" : 5
}
},
{
"key" : "Dillard's",
"doc_count" : 3,
"sales_count" : {
"value" : 3
}
},
{
"key" : "Target",
"doc_count" : 3,
"sales_count" : {
"value" : 3
}
},
{
"key" : "Harrod's",
"doc_count" : 2,
"sales_count" : {
"value" : 2
}
},
{
"key" : "Men's Warehouse",
"doc_count" : 2,
"sales_count" : {
"value" : 2
}
},
{
"key" : "Sears",
"doc_count" : 1,
"sales_count" : {
"value" : 1
}
}
]
},
"avg sales at a store" : {
"value" : 3.142857142857143
}
}
所以每家商店平均售出的帽子数量是 3.1 顶,而不是 4.6 顶。但在buckets
数组中,我只想查看前 3 家商店。
解决方案
您可以在没有管道聚合的情况下实现您的目标。它有点欺骗聚合框架,但是,它有效。
这是数据设置:
PUT hat_sales
{
"mappings": {
"properties": {
"storename": {
"type": "keyword"
}
}
}
}
POST hat_sales/_bulk?refresh=true
{"index": {}}
{"storename": "foo"}
{"index": {}}
{"storename": "foo"}
{"index": {}}
{"storename": "bar"}
{"index": {}}
{"storename": "baz"}
{"index": {}}
{"storename": "baz"}
{"index": {}}
{"storename": "baz"}
这是一个棘手的查询:
GET hat_sales/_search?size=0
{
"aggs": {
"stores": {
"terms": {
"field": "storename",
"size": 2
}
},
"average_sales_count": {
"avg_bucket": {
"buckets_path": "stores>_count"
}
},
"cheat": {
"filters": {
"filters": {
"all": {
"exists": {
"field": "storename"
}
}
}
},
"aggs": {
"count": {
"value_count": {
"field": "storename"
}
},
"unique_count": {
"cardinality": {
"field": "storename"
}
},
"total_average": {
"bucket_script": {
"buckets_path": {
"total": "count",
"unique": "unique_count"
},
"script": "params.total / params.unique"
}
}
}
}
}
}
这是对 aggs 框架的一个小滥用。但是,这个想法是你实际上想要num_stores/num_docs
. 我将其限制num_docs
为仅具有实际storefield
名称的文档。
我通过使用过滤器agg 进行了一些验证,这在技术上是一个多桶 agg(尽管我只关心一个桶)。
然后我通过基数(num stores)和总计数(value_count)获得唯一计数,并使用 abucket_script
来完成它。
总而言之,这是略微损坏的结果:D
{
"took" : 3,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 6,
"relation" : "eq"
},
"max_score" : null,
"hits" : [ ]
},
"aggregations" : {
"cheat" : {
"buckets" : {
"all" : {
"doc_count" : 6,
"count" : {
"value" : 6
},
"unique_count" : {
"value" : 3
},
"total_average" : {
"value" : 2.0
}
}
}
},
"stores" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 1,
"buckets" : [
{
"key" : "baz",
"doc_count" : 3
},
{
"key" : "foo",
"doc_count" : 2
}
]
},
"average_sales_count" : {
"value" : 2.5
}
}
}
请注意,cheat.buckets.all.total_average
是2.0
(真实平均值),而旧方法(管道平均值)是非全局平均值2.5
推荐阅读
- python - 按特定顺序为每个唯一 ID 连接多个字符串行
- mongodb - 如何找到包含具有两个相等值的数组的文档?
- angular - 路由错误 - 类型“字符串”不可分配给类型“类型”
' - python - 连接两个 2D 张量
- python-3.x - 无法成功运行“func host start”来测试 azure 函数
- shell - 监视日志文件中的字符串,然后从同一行创建变量以处理所述文件
- google-cloud-run - GCP 托管的 Cloud Run 的出站 IP 范围是多少?
- java - 错误:尝试在空对象引用上调用虚拟方法...
- .net - 获取在自定义用户控件中声明的事件的所有事件处理程序
- php - Laravel Dusk - 无法在 GitLab CI 上运行