我的数据库中的对象看起来像这样:
{
"_id": ObjectId("563f8c320ef987c122aeeb4a"),
"num": 1515,
"createdAt": ISODate("2015-10-29T21:14:26.477Z"),
}
我希望编写一个聚合,按特定ID对所有内容进行分组,并汇总今天,本周和本月的总计,并在一个查询中执行。我为此任务编写了三个单独的查询,但我想知道我是否可以提高效率并在一次查询中完成。
修改
有人提到mapReduce作为解决方案。这似乎是一个很有前途的解决方案,但我无法从简单的查询中获得任何回报。以下是我的尝试:
var o = {};
o.map = function () { emit( this.num, this.createdAt ) }
o.reduce = function (k, vals) { return vals }
o.query = {
_id: req.user._id
}
Submission.mapReduce(o, function (err, results) {
console.log(results)
})
控制台记录一个空数组。我也尝试将_id
转换为mongoose对象id,但它仍然返回一个空数组。
答案 0 :(得分:3)
这更像是一个你期望输出看起来像什么的问题,因为任何聚合结果基本上都需要在最低级别进行分组,然后逐步分组在更高的" grain"直到达到最大水平("月")。这种意味着按"月"分组的数据。最终,除非你打破它。否则。
实质上,逐步$group
:
db.collection.aggregate([
// First total per day. Rounding dates with math here
{ "$group": {
"_id": {
"$add": [
{ "$subtract": [
{ "$subtract": [ "$createdAt", new Date(0) ] },
{ "$mod": [
{ "$subtract": [ "$createdAt", new Date(0) ] },
1000 * 60 * 60 * 24
]}
]},
new Date(0)
]
},
"week": { "$first": { "$week": "$createdAt" } },
"month": { "$first": { "$month": "$createdAt" } },
"total": { "$sum": "$num" }
}},
// Then group by week
{ "$group": {
"_id": "$week",
"month": { "$first": "$month" },
"days": {
"$push": {
"day": "$_id",
"total": "$total"
}
},
"total": { "$sum": "$total" }
}},
// Then group by month
{ "$group": {
"_id": "$month",
"weeks": {
"$push": {
"week": "$_id",
"total": "$total",
"days": "$days"
}
},
"total": { "$sum": "$total" }
}}
])
因此,每天在总和之后的每个级别逐渐被推入到数组内容中,以便它可以向上"向上"然后,价值和总数也会在该水平上相加。
如果你想要一个更平坦的输出,每天有一条记录包含它的每周和每月总数以及总日数,那么只需在管道的末尾添加两个$unwind
语句:
{ "$unwind": "$weeks" },
{ "$unwind": "$weeks.days" }
可选$project
"点缀"如果你必须这样做,就会变得更加平坦和可读。
如果你正在跨越"岁月"有了这个,那么至少从"周"在分组键中包括这样的操作。因此,您不可能组合来自不同年份的数据,并将它们分开。
我自己一般倾向于在舍入日期时使用"date math"方法,因为它返回Date
个对象,但是在其他级别使用{" day" ,您可以改为使用date aggregation operators代替。
不需要mapReduce
,因为这是相当直观的,并且一个月内的天数有限,这意味着在内容中嵌套数组时BSON限制,而聚合不会被破坏。