使用 setWindowFields 计算值的变化



>我在此架构中有一大组记录:

{
username: "name@email.com",
timestamp: 1646006400000 //unix timestamp in milliseconds
amount: 100
}

给定任意日期范围,我需要找到平均金额值与类似的先前日期范围的变化。

例如,如果输入是 3 月 10 日至 3 月 15 日,我需要计算该期间的平均值以及与 3 月 5 日至 3 月 10 日期间的平均值的差值。

这是我的计划:

  1. 给定from_date&to_date,计算间隔并减去得到prior_from_date&prior_to_date
  2. matchprior_from_dateto_date的所有记录
  3. 使用 setWindowFields 创建prior先前记录的标志
  4. usernamegroup,然后按标志分组prior以计算平均值

我可以在应用程序端 (PHP) 上计算步骤 1。我能够使用$avg获得平均值,但无法弄清楚如何执行第 3 步。这是我到目前为止所拥有的:

{
partitionBy: '$username',
sortBy: {
timestamp: 1
},
output: {
prior: {
$sum: '$prior',
window: {
range: [
-86400000, // milliseconds in a day - may be multiply by interval from PHP?
0
]
}
}
}
}

这是最终所需的输出:

{
{
username:"name@email.com",
prior: {
average: 50
},
current: {
average: 74
}
},
{
username:"name2@email.com",
prior: {
average: 73
},
current: {
average: 33
}
}
}

你的要求对我来说不是很清楚,但可能是这个:

db.collection.aggregate([
{ $set: { timestamp: { $toDate: "$timestamp" } } },
{
$setWindowFields: {
partitionBy: "$username",
sortBy: { timestamp: 1 },
output: {
average: {
$avg: "$amount",
window: { range: [-5, 5], unit: "day" }
}
}
}
}
])

由于您使用的是$match步骤,并且已经计算了时间戳,因此您可以使用具有条件的$addFields进行分组以获得相同的效果:

db.collection.aggregate([
{
$match: {
timestamp: {
$gte: 1646438400000,
$lt: 1647302400000
}
}
},
{
"$addFields": {
"period": {
"$cond": [
{$gt: ["$timestamp", 1646870400000]},
"current",
"prior"
]
}
}
},
{
$group: {
_id: {period: "$period", username: "$username"},
average: {$avg: "$amount"}
}
},
{
$project: {
username: "$_id.username",
"data.k": "$_id.period",
"data.v": {average: "$average"},
_id: 0
}
},
{
$group: {_id: "$username",
data: {$push: "$data"}
}
},
{
$project: {
"data": {"$arrayToObject": "$data"}
}
},
{
$project: {
_id: 1,
current: "$data.current",
prior: "$data.prior"
}
}
])

正如你在操场上看到的: 返回以下内容:

[
{
"_id": "name@email.com",
"current": {
"average": 42.5
},
"prior": {
"average": 68
}
},
{
"_id": "name2@email.com",
"current": {
"average": 55
},
"prior": {
"average": 37
}
}
]

最新更新