我有一个包含字段user_id、amount和Category的索引交易。我想计算每个用户和类别的平均金额,然后仅获取每个类别的平均总金额。SQL 看起来像这样:
SELET AVG(average), category from
(SELECT user_id, category, AVG(amount) AS average FROM transactions WHERE amount < 100000
GROUP BY user_id, category) AS a1
GROUP BY category
Run Code Online (Sandbox Code Playgroud)
我目前只得到一个包含所有用户 ID 的存储桶的响应,然后在其中包含一个包含每个类别(针对用户)的平均金额的存储桶。我不明白如何添加另一个聚合来完成我想要的事情。
{
"aggs": {
"group_by_users": {
"terms": {
"field": "user_id.keyword"
},
"aggs": {
"group_by_category": {
"terms": {
"field": "category.keyword"
},
"aggs": {
"average_amount": {
"avg": {
"field": "amount"
}
}
}
}
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
很感谢任何形式的帮助。
编辑:请求示例,因此这里首先是一些示例数据,然后是中间结果,中间结果将以底部的所需结果结束。
-----------------------------------------
| user_id | category | amount |
-----------------------------------------
| 1 | insurances | 1000 |
| 1 | transport | 50 |
| 1 | transport | 100 |
| 2 | insurances | 700 |
| 2 | insurances | 200 |
| 2 | transport | 300 |
-----------------------------------------
Run Code Online (Sandbox Code Playgroud)
用户1传输的计算:(50+100)/2
因此,需要做的第一件事是按 user_id 和类别进行分组,以获得每个用户和类别的平均值。
这会产生:
-----------------------------------------
| user_id | category | average |
-----------------------------------------
| 1 | insurances | 1000 |
| 1 | transport | 75 |
| 2 | insurances | 450 |
| 2 | transport | 300 |
-----------------------------------------
Run Code Online (Sandbox Code Playgroud)
重要的是要了解我无法对所有用户进行平均,我首先需要每个用户、每个类别的平均支出。
所以现在我只想按类别分组并计算平均金额:
-----------------------------
| category | average |
-----------------------------
| insurances | 725 |
| transport | 187,5 |
-----------------------------
Run Code Online (Sandbox Code Playgroud)
保险示例:(1000 + 450) / 2
avg_bucket在某些情况下,管道聚合可以完成这项工作(但可能无法随着索引的大小很好地扩展,请参阅下面的注释):
POST myindex1/_search
{
"size": 0,
"aggs": {
"by category": {
"terms": {
"field": "category.keyword"
},
"aggs": {
"by user_id": {
"terms": {
"field": "user_id"
},
"aggs": {
"avg by user": {
"avg": {
"field": "amount"
}
}
}
},
"average by user, category": {
"avg_bucket": {
"buckets_path": "by user_id>avg by user"
}
}
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
这将响应看起来像这样:
{
...
"aggregations" : {
"by category" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "insurances",
"doc_count" : 3,
"by user_id" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : 2,
"doc_count" : 2,
"avg by user" : {
"value" : 450.0
}
},
{
"key" : 1,
"doc_count" : 1,
"avg by user" : {
"value" : 1000.0
}
}
]
},
"average by user, category" : {
"value" : 725.0 <--- average for `insurances`
}
},
{
"key" : "transport",
"doc_count" : 3,
"by user_id" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : 1,
"doc_count" : 2,
"avg by user" : {
"value" : 75.0
}
},
{
"key" : 2,
"doc_count" : 1,
"avg by user" : {
"value" : 300.0
}
}
]
},
"average by user, category" : {
"value" : 187.5 <--- average for `transport`
}
}
]
}
}
}
Run Code Online (Sandbox Code Playgroud)
让我们从"by user_id" terms聚合开始:我们要求 Elasticsearch 对文档进行分组并使用聚合user_id计算平均值。amountavg
POST myindex1/_search
{
"size": 0,
"aggs": {
"by user_id": {
"terms": {
"field": "user_id"
},
"aggs": {
"avg by user": {
"avg": {
"field": "amount"
}
}
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
这相当于 SQL:
SELECT user_id, avg(amount)
FROM my_index
GROUP BY user_id;
Run Code Online (Sandbox Code Playgroud)
到目前为止,这非常简单。但现在我们如何才能在该类别上进行平均呢?
我们可以在一个聚合之上添加另一个terms聚合。现在还将考虑类别:"by category""by user id"avg
POST myindex1/_search
{
"size": 0,
"aggs": {
"by category": {
"terms": {
"field": "category.keyword"
},
"aggs": {
"by user_id": {
"terms": {
"field": "user_id"
},
"aggs": {
"avg by user": {
"avg": {
"field": "amount"
}
}
}
}
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
这相当于 SQL:
SELECT user_id, category, avg(amount)
FROM my_index
GROUP BY user_id, category;
Run Code Online (Sandbox Code Playgroud)
我们现在可以使用上一个查询的结果并category再次聚合吗?
这可以通过管道聚合来完成avg_bucket。唯一缺少的是告诉avg_bucket聚合在哪里准确找到要聚合的存储桶,这是通过buckets_path表达式完成的。
这就是我们如何到达我在顶部发布的查询,它将有效地执行与您在问题中发布的 SQL 等效的操作。
但...
此方法的缺点是它不能很好地适应索引中文档的数量。
事实上,管道聚合仅对已经聚合的数据进行操作:
管道聚合作用于其他聚合(而不是文档集)生成的输出,从而将信息添加到输出树中。
user_id在我们的例子中,这意味着如果索引中的不同项超过 10 个,我们的平均值将不准确。
发生这种情况是因为默认情况下terms聚合仅返回前 10 个存储桶,并且 SQL 等价物应如下所示:
SELECT category, avg(avg_amount)
FROM (
SELECT user_id, category, avg(amount) avg_amount
FROM my_index
GROUP BY user_id, category
LIMIT 10 per user_id
) Q
LIMIT 10;
Run Code Online (Sandbox Code Playgroud)
size可以通过聚合参数更改此限制terms。
另一件需要记住的事情是terms返回近似的文档计数,这也会影响平均值。
希望有帮助!
| 归档时间: |
|
| 查看次数: |
4693 次 |
| 最近记录: |