Questa è più realmente una questione di come ti aspetti che appaia l'output, poiché qualsiasi risultato aggregato deve essenzialmente raggrupparsi al livello più basso e quindi raggruppare progressivamente a "grani" più alti fino a raggiungere il livello più grande ("mese" ). Questo tipo di implica che i dati siano raggruppati per "mese", a meno che tu non li scomponga diversamente.
In sostanza, progressivamente $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" }
}}
])
Quindi ogni livello dopo il primo che somma per giorno viene quindi progressivamente inserito nel contenuto dell'array per il suo valore "arrotondato per eccesso" e anche i totali vengono sommati a quel livello.
Se desideri un output più piatto con un record al giorno contenente i totali settimanali e mensili e il totale giornaliero, aggiungi semplicemente due $unwind
dichiarazioni alla fine della pipeline:
{ "$unwind": "$weeks" },
{ "$unwind": "$weeks.days" }
E facoltativamente $project
i campi "punteggiati" diventano qualcosa di più piatto e leggibile, se necessario.
Se stai coprendo "anni" con questo, includi tale operazione nella chiave di raggruppamento almeno dal livello "settimana", in modo da non combinare dati di anni diversi e che siano separati.
È anche mia preferenza generale usare "date math"
approccio durante l'arrotondamento delle date poiché restituisce una Date
oggetto, ma poiché viene utilizzato agli altri livelli rispetto a "giorno", puoi semplicemente utilizzare alternativamente operatori di aggregazione di date
invece.
Non c'è bisogno di mapReduce
poiché questo è abbastanza intuitivo e c'è un numero limitato di giorni in un mese, il che significa che il limite BSON durante l'annidamento di array nel contenuto durante l'aggregazione non verrà interrotto.