Dit is meer een kwestie van hoe u verwacht dat de uitvoer eruit zal zien, aangezien elk geaggregeerd resultaat in wezen op het laagste niveau moet worden gegroepeerd en vervolgens geleidelijk moet worden gegroepeerd op hogere "korrels" totdat het grootste niveau ("maand") is bereikt. Dit soort gegevens houdt uiteindelijk in gegroepeerd op "maand", tenzij u ze anders opsplitst.
In wezen geleidelijk $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" }
}}
])
Dus elk niveau na het eerste dat per dag wordt opgeteld, wordt vervolgens progressief in de array-inhoud geduwd voor zijn "afgeronde" waarde en de totalen worden vervolgens ook op dat niveau opgeteld.
Als u een vlakkere uitvoer wilt met één record per dag met daarin zowel de wekelijkse en maandelijkse totalen als het dagtotaal, voeg dan gewoon twee $unwind
toe verklaringen aan het einde van de pijplijn:
{ "$unwind": "$weeks" },
{ "$unwind": "$weeks.days" }
En optioneel $project
de "gestippelde" velden naar iets platter en leesbaarder als het moet.
Als u hiermee "jaren" overspant, neem dan een dergelijke bewerking op in de groeperingssleutel ten minste vanaf het "week"-niveau, zodat u onmogelijk gegevens van verschillende jaren combineert en ze worden gescheiden.
Het is ook mijn eigen algemene voorkeur om de "date math"
te gebruiken benadering bij het afronden van datums omdat het een Date
oplevert object, maar zoals wordt gebruikt op de andere niveaus dan "dag", kunt u gewoon afwisselend de operators voor datumaggregatie
in plaats daarvan.
Geen behoefte aan mapReduce
omdat dit redelijk intuïtief is en er een eindig aantal dagen in een maand is, betekent dit dat de BSON-limiet bij het nesten van arrays in de inhoud tijdens het aggregeren niet wordt verbroken.