MongoDB
 sql >> Database >  >> NoSQL >> MongoDB

Mongodb:interroga i totali di oggi, i totali della settimana e i totali del mese in un'unica query

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.