样本文件:
{
_id: ObjectId('4f442120eb03305789000000'),
time: ISODate("2013-10-10T20:55:36Z"),
value:1
},
{
_id: ObjectId('4f442120eb03305789000001'),
time: ISODate("2013-10-10T28:43:16Z"),
value:2
},
{
_id: ObjectId('4f442120eb03305789000002'),
time: ISODate("2013-10-11T27:12:66Z"),
value:3
},
{
_id: ObjectId('4f442120eb03305789000003'),
time: ISODate("2013-10-11T10:15:38Z"),
value:4
},
{
_id: ObjectId('4f442120eb03305789000004'),
time: ISODate("2013-10-12T26:15:38Z"),
value:5
}
很容易获得按日期分组的汇总结果。 但我想要的是查询返回运行总计的结果 汇总,如:
{
time: "2013-10-10"
total: 3,
runningTotal: 3
},
{
time: "2013-10-11"
total: 7,
runningTotal: 10
},
{
time: "2013-10-12"
total: 5,
runningTotal: 15
}
MongoDB Aggregation可以实现吗?
答案 0 :(得分:5)
这可以满足您的需求。我已将数据中的时间标准化,因此它们组合在一起(您可以执行this之类的操作)。我的想法是$group
并将time
和total
推送到单独的数组中。然后$unwind
time
数组,并为每个totals
文档制作了time
数组的副本。然后,您可以从包含不同时间的所有数据的数组计算runningTotal
(或类似滚动平均值)。 '指数'由$unwind
生成的是与total
对应的time
的数组索引。在$sort
之前$unwind
非常重要,因为这样可以确保数组的顺序正确。
db.temp.aggregate(
[
{
'$group': {
'_id': '$time',
'total': { '$sum': '$value' }
}
},
{
'$sort': {
'_id': 1
}
},
{
'$group': {
'_id': 0,
'time': { '$push': '$_id' },
'totals': { '$push': '$total' }
}
},
{
'$unwind': {
'path' : '$time',
'includeArrayIndex' : 'index'
}
},
{
'$project': {
'_id': 0,
'time': { '$dateToString': { 'format': '%Y-%m-%d', 'date': '$time' } },
'total': { '$arrayElemAt': [ '$totals', '$index' ] },
'runningTotal': { '$sum': { '$slice': [ '$totals', { '$add': [ '$index', 1 ] } ] } },
}
},
]
);
我在一个集合中使用了类似的东西,大约有80 000个文档,总计达到63个结果。我不确定它对大型集合的效果如何,但我发现,一旦数据减少到可管理的大小,对聚合数据执行转换(投影,数组操作)似乎没有很大的性能成本。
答案 1 :(得分:2)
这是另一种方法
管道
db.col.aggregate([
{$group : {
_id : { time :{ $dateToString: {format: "%Y-%m-%d", date: "$time", timezone: "-05:00"}}},
value : {$sum : "$value"}
}},
{$addFields : {_id : "$_id.time"}},
{$sort : {_id : 1}},
{$group : {_id : null, data : {$push : "$$ROOT"}}},
{$addFields : {data : {
$reduce : {
input : "$data",
initialValue : {total : 0, d : []},
in : {
total : {$sum : ["$$this.value", "$$value.total"]},
d : {$concatArrays : [
"$$value.d",
[{
_id : "$$this._id",
value : "$$this.value",
runningTotal : {$sum : ["$$value.total", "$$this.value"]}
}]
]}
}
}
}}},
{$unwind : "$data.d"},
{$replaceRoot : {newRoot : "$data.d"}}
]).pretty()
集合
> db.col.find()
{ "_id" : ObjectId("4f442120eb03305789000000"), "time" : ISODate("2013-10-10T20:55:36Z"), "value" : 1 }
{ "_id" : ObjectId("4f442120eb03305789000001"), "time" : ISODate("2013-10-11T04:43:16Z"), "value" : 2 }
{ "_id" : ObjectId("4f442120eb03305789000002"), "time" : ISODate("2013-10-12T03:13:06Z"), "value" : 3 }
{ "_id" : ObjectId("4f442120eb03305789000003"), "time" : ISODate("2013-10-11T10:15:38Z"), "value" : 4 }
{ "_id" : ObjectId("4f442120eb03305789000004"), "time" : ISODate("2013-10-13T02:15:38Z"), "value" : 5 }
结果
{ "_id" : "2013-10-10", "value" : 3, "runningTotal" : 3 }
{ "_id" : "2013-10-11", "value" : 7, "runningTotal" : 10 }
{ "_id" : "2013-10-12", "value" : 5, "runningTotal" : 15 }
>
答案 2 :(得分:2)
这是一个解决方案,无需将以前的文档推入新的数组然后进行处理。 (如果数组太大,则可以超过BSON文档的最大大小限制,即16MB。)
计算运行总计很简单:
db.collection1.aggregate(
[
{
$lookup: {
from: 'collection1',
let: { date_to: '$time' },
pipeline: [
{
$match: {
$expr: {
$lt: [ '$time', '$$date_to' ]
}
}
},
{
$group: {
_id: null,
summary: {
$sum: '$value'
}
}
}
],
as: 'sum_prev_days'
}
},
{
$addFields: {
sum_prev_days: {
$arrayElemAt: [ '$sum_prev_days', 0 ]
}
}
},
{
$addFields: {
running_total: {
$sum: [ '$value', '$sum_prev_days.summary' ]
}
}
},
{
$project: { sum_prev_days: 0 }
}
]
)
我们做了什么:在查找中,我们选择了日期时间较小的所有文档,并立即计算了总和(使用$ group作为查找流程的第二步)。 $ lookup将值放入数组的第一个元素。我们提取第一个数组元素,然后计算总和:当前值+先前值的总和。
如果您想将交易分组为几天,并在计算出运行总额之后,则需要在开始处插入$ group,并将其插入$ lookup的管道中。
db.collection1.aggregate(
[
{
$group: {
_id: {
$substrBytes: ['$time', 0, 10]
},
value: {
$sum: '$value'
}
}
},
{
$lookup: {
from: 'collection1',
let: { date_to: '$_id' },
pipeline: [
{
$group: {
_id: {
$substrBytes: ['$time', 0, 10]
},
value: {
$sum: '$value'
}
}
},
{
$match: {
$expr: {
$lt: [ '$_id', '$$date_to' ]
}
}
},
{
$group: {
_id: null,
summary: {
$sum: '$value'
}
}
}
],
as: 'sum_prev_days'
}
},
{
$addFields: {
sum_prev_days: {
$arrayElemAt: [ '$sum_prev_days', 0 ]
}
}
},
{
$addFields: {
running_total: {
$sum: [ '$value', '$sum_prev_days.summary' ]
}
}
},
{
$project: { sum_prev_days: 0 }
}
]
)
结果是:
{ "_id" : "2013-10-10", "value" : 3, "running_total" : 3 }
{ "_id" : "2013-10-11", "value" : 7, "running_total" : 10 }
{ "_id" : "2013-10-12", "value" : 5, "running_total" : 15 }