C'est plus vraiment une question de ce à quoi vous vous attendez à ce que la sortie ressemble, car tout résultat agrégé doit essentiellement être regroupé au niveau le plus bas, puis progressivement regroupé à des "grains" plus élevés jusqu'à ce que le niveau le plus élevé ("mois") soit atteint. Ce type implique des données regroupées par "mois" en fin de compte, sauf si vous les décomposez autrement.
Essentiellement, progressivement $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" }
}}
])
Ainsi, chaque niveau après le premier qui résume par jour est ensuite progressivement poussé dans le contenu du tableau pour sa valeur "arrondie" et les totaux sont ensuite additionnés à ce niveau également.
Si vous voulez une sortie plus plate avec un enregistrement par jour contenant ses totaux hebdomadaires et mensuels ainsi que le total de la journée, ajoutez simplement deux $unwind
instructions jusqu'à la fin du pipeline :
{ "$unwind": "$weeks" },
{ "$unwind": "$weeks.days" }
Et éventuellement $project
les champs "pointillés" vers quelque chose de plus plat et lisible si vous le devez.
Si vous couvrez des "années" avec cela, incluez une telle opération dans la clé de regroupement au moins à partir du niveau "semaine" afin de ne pas combiner des données de différentes années et qu'elles soient séparées.
C'est aussi ma propre préférence générale pour utiliser le "date math"
approche lors de l'arrondi des dates car il renvoie une Date
object, mais comme il est utilisé aux autres niveaux que "day", vous pouvez simplement utiliser alternativement le opérateurs d'agrégation de dates
à la place.
Pas besoin de mapReduce
car c'est assez intuitif et il y a un nombre fini de jours dans un mois, ce qui signifie que la limite BSON lors de l'imbrication de tableaux dans le contenu lors de l'agrégation ne sera pas dépassée.