Dies ist eher eine Frage der erwarteten Ausgabe, da jedes aggregierte Ergebnis im Wesentlichen auf der niedrigsten Ebene gruppiert und dann schrittweise auf höheren "Körnern" gruppiert werden muss, bis die größte Ebene ("Monat") erreicht ist. Diese Art von Daten impliziert, dass sie letztendlich nach "Monat" gruppiert sind, sofern Sie sie nicht anders aufschlüsseln.
Im Wesentlichen schrittweise $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" }
}}
])
Jede Ebene nach der ersten, die pro Tag summiert wird, wird dann nach und nach für ihren "Aufrundungswert" in den Array-Inhalt geschoben, und die Gesamtsummen werden dann auch auf dieser Ebene summiert.
Wenn Sie eine flachere Ausgabe mit einem Datensatz pro Tag wünschen, der die Wochen- und Monatssummen sowie die Tagessumme enthält, hängen Sie einfach zwei $unwind
an Anweisungen an das Ende der Pipeline:
{ "$unwind": "$weeks" },
{ "$unwind": "$weeks.days" }
Und optional $project
die "gepunkteten" Felder etwas flacher und lesbarer, wenn Sie müssen.
Wenn Sie damit "Jahre" überspannen, nehmen Sie eine solche Operation mindestens ab der Ebene "Woche" in den Gruppierungsschlüssel auf, damit Sie möglicherweise keine Daten aus verschiedenen Jahren kombinieren und diese trennen.
Es ist auch meine eigene allgemeine Präferenz, den "date math"
zu verwenden Ansatz beim Runden von Datumsangaben, da er ein Date
zurückgibt -Objekt, aber wie es auf anderen Ebenen als „Tag“ verwendet wird, können Sie einfach alternativ das Datumsaggregationsoperatoren
stattdessen.
Keine Notwendigkeit für mapReduce
Da dies ziemlich intuitiv ist und es eine begrenzte Anzahl von Tagen in einem Monat gibt, bedeutet dies, dass die BSON-Grenze beim Verschachteln von Arrays im Inhalt während der Aggregation nicht überschritten wird.