Detta är mer egentligen en fråga om hur du förväntar dig att produktionen ska se ut, eftersom alla aggregerade resultat i huvudsak måste grupperas på den lägsta nivån och sedan successivt grupperas på högre "korn" tills den största nivån ("månad") nås. Denna typ av innebär data grupperade efter "månad" i slutändan, om du inte bryter ner den på annat sätt.
I huvudsak, successivt $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" }
}}
])
Så varje nivå efter den första som summerar per dag skjuts sedan successivt in i arrayinnehållet för dess "avrundningsvärde" och totalsummorna summeras sedan på den nivån också.
Om du vill ha en plattare utdata med en post per dag som innehåller vecko- och månadssummor samt dagsumman, lägg bara till två $unwind
uttalanden till slutet av pipelinen:
{ "$unwind": "$weeks" },
{ "$unwind": "$weeks.days" }
Och eventuellt $project
de "prickade" fälten ut till något plattare och läsbart om du måste.
Om du spänner över "år" med detta, inkludera en sådan operation i grupperingsnyckeln åtminstone från "vecka"-nivån så att du inte möjligen kombinerar data från olika år och de separeras.
Det är också min egen allmänna preferens att använda "date math"
när du avrundar datum eftersom det returnerar ett Date
objekt, men som används på andra nivåer än "dag", kan du bara alternativt använda operatorer för datumaggregation
istället.
Inget behov av mapReduce
eftersom detta är ganska intuitivt och det finns ett ändligt antal dagar i en månad som innebär att BSON-gränsen när kapsling av arrayer i innehållet medan aggregering inte kommer att brytas.