MongoDB
 sql >> Datenbank >  >> NoSQL >> MongoDB

Mongo / Mongoose - Aggregieren nach Datum

Ein guter Ansatz wäre, die Aggregationspipeline in mehrere Schritte aufzuteilen, mit dem Ziel, die Aggregate für jede Gruppe zu berechnen, d. h. jährliche, monatliche und wöchentliche Aggregate.

Ich habe einen schwachen Versuch unternommen, die besagte Pipeline zu generieren, bin mir aber nicht sicher, ob Sie danach suchen, könnte Ihnen aber einige Hinweise zu einer Lösung geben, besser noch zu einer optimalen. Vielleicht könnte jemand anderes eine bessere Antwort geben.

Betrachten Sie Folgendes als ungetestet Leitung:

db.statements.aggregate([
    {
        "$group": {
            "_id": {
                "name": "$name",
                "year": { "$year": "$date" },
                "month": { "$month": "$date" },
                "week": { "$week": "$date" }
            },
            "total": { "$sum": "$amount" }
        }
    },
    {
        "$group": {
            "_id": {
                "name": "$_id.name",
                "year": "$_id.year"
            },
            "YearlySpends": { "$push": "$total" },
            "totalYearlyAmount": { "$sum": "$total" },
            "data": { "$push": "$$ROOT" }
        }
    },
    { "$unwind": "$data" },
    {
        "$group": {
            "_id": {
                "name": "$_id.name",
                "month": "$data._id.month"
            },
            "YearlySpends": { "$first": "$YearlySpends" },
            "totalYearlyAmount": { "$first": "$totalYearlyAmount" },
            "MonthlySpends": { "$push": "$data.total" },
            "totalMonthlyAmount": { "$sum": "$data.total" },
            "data": { "$push": "$data" }
        }
    },
    { "$unwind": "$data" },
    {
        "$group": {
            "_id": {
                "name": "$_id.name",
                "week": "$data._id.week"
            },
            "YearlySpends": { "$first": "$YearlySpends" },
            "totalYearlyAmount": { "$first": "$totalYearlyAmount" },
            "MonthlySpends": { "$first": "$MonthlySpends" },
            "totalMonthlyAmount": { "$first": "$totalMonthlyAmount" },
            "WeeklySpends": { "$push": "$data.total" },
            "totalWeeklyAmount": { "$sum": "$data.total" },
            "data": { "$push": "$data" }
        }
    },
    { "$unwind": "$data" },
    {
        "$group": {
            "_id": "$data._id",
            "YearlySpends": { "$first": "$YearlySpends" },
            "totalYearlyAmount": { "$first": "$totalYearlyAmount" },
            "MonthlySpends": { "$first": "$MonthlySpends" },
            "totalMonthlyAmount": { "$first": "$totalMonthlyAmount" },
            "WeeklySpends": { "$first": "$WeeklySpends" },
            "totalWeeklyAmount": { "$first": "$totalWeeklyAmount" }
        }
    }
])

Beispielausgabe

/* 1 */
{
    "_id" : {
        "name" : "Tesco",
        "year" : 2017,
        "month" : 3,
        "week" : 11
    },
    "YearlySpends" : [ 
        -3.3
    ],
    "totalYearlyAmount" : -3.3,
    "MonthlySpends" : [ 
        -3.3
    ],
    "totalMonthlyAmount" : -3.3,
    "WeeklySpends" : [ 
        -3.3
    ],
    "totalWeeklyAmount" : -3.3
}

/* 2 */
{
    "_id" : {
        "name" : "RINGGO",
        "year" : 2017,
        "month" : 4,
        "week" : 17
    },
    "YearlySpends" : [ 
        -3.3, 
        -26.3, 
        -33.3
    ],
    "totalYearlyAmount" : -62.9,
    "MonthlySpends" : [ 
        -33.3
    ],
    "totalMonthlyAmount" : -33.3,
    "WeeklySpends" : [ 
        -33.3
    ],
    "totalWeeklyAmount" : -33.3
}

/* 3 */
{
    "_id" : {
        "name" : "RINGGO",
        "year" : 2017,
        "month" : 3,
        "week" : 12
    },
    "YearlySpends" : [ 
        -3.3, 
        -26.3, 
        -33.3
    ],
    "totalYearlyAmount" : -62.9,
    "MonthlySpends" : [ 
        -3.3, 
        -26.3
    ],
    "totalMonthlyAmount" : -29.6,
    "WeeklySpends" : [ 
        -3.3
    ],
    "totalWeeklyAmount" : -3.3
}

/* 4 */
{
    "_id" : {
        "name" : "RINGGO",
        "year" : 2017,
        "month" : 3,
        "week" : 11
    },
    "YearlySpends" : [ 
        -3.3, 
        -26.3, 
        -33.3
    ],
    "totalYearlyAmount" : -62.9,
    "MonthlySpends" : [ 
        -3.3, 
        -26.3
    ],
    "totalMonthlyAmount" : -29.6,
    "WeeklySpends" : [ 
        -26.3
    ],
    "totalWeeklyAmount" : -26.3
}

/* 5 */
{
    "_id" : {
        "name" : "Sky",
        "year" : 2017,
        "month" : 3,
        "week" : 9
    },
    "YearlySpends" : [ 
        -63.3
    ],
    "totalYearlyAmount" : -63.3,
    "MonthlySpends" : [ 
        -63.3
    ],
    "totalMonthlyAmount" : -63.3,
    "WeeklySpends" : [ 
        -63.3
    ],
    "totalWeeklyAmount" : -63.3
}

/* 6 */
{
    "_id" : {
        "name" : "Amazon",
        "year" : 2017,
        "month" : 3,
        "week" : 12
    },
    "YearlySpends" : [ 
        -61.3
    ],
    "totalYearlyAmount" : -61.3,
    "MonthlySpends" : [ 
        -61.3
    ],
    "totalMonthlyAmount" : -61.3,
    "WeeklySpends" : [ 
        -61.3
    ],
    "totalWeeklyAmount" : -61.3
}

AKTUALISIEREN

Wenn Sie Filter in die Aggregatoperation einbeziehen möchten, würde ich vorschlagen, dass Sie $match Abfrage als erste Pipelinestufe. Wenn jedoch ein anfänglicher $match Schritt, dann würden die vorherigen Schritte leicht geändert, da Sie gefilterte Ergebnisse aggregieren werden, ganz anders als anfänglich alle Dokumente als Ganzes zu aggregieren und dann den Filter auf die Ergebnisse anzuwenden.

Wenn Sie den Filter-zuerst-dann-Aggregation nehmen sollen route, ziehen Sie in Betracht, eine Aggregatoperation auszuführen, die verwendet $match als erster Schritt, der die Dokumente nach Anbieter filtert, dann ein vorangestelltes $redact Pipeline-Schritt, um die Dokumente weiter nach dem Monatsteil des Datumsfelds zu filtern, und dann wäre der Rest der $group Stufen:

Statements.aggregate([
    { "$match": { "name": req.params.vendor } },
    {
        "$redact": {
            "$cond": [
                { "$eq": [{ "$month": "$date" }, parseInt(req.params.month) ]},
                "$$KEEP",
                "$$PRUNE"
            ]
        }
    },
    .....
    /*
        add the remaining pipeline steps after
    */
], function(err, data){
    if (err) throw err;
    console.log(data);
})

Wenn Sie den Gruppen-zuerst-dann-Filter nehmen sollen route, dann wäre der Filter nach der letzten Pipeline, die das gruppierte Ergebnis liefert, aber auf andere Felder angewendet wird, da die Dokumente in diesem Teil des Streams sich vom ursprünglichen Schema unterscheiden würden.

Diese Route ist nicht leistungsfähig, da Sie den Aggregationsvorgang mit allen Dokumenten in der Sammlung beginnen und anschließend filtern:

Statements.aggregate([
    .....
    /*
        place the initial pipeline steps from 
        the original query above here
    */
    .....
    { 
        "$match": { 
            "_id.name": req.params.vendor,
            "_id.month": parseInt(req.params.month)
        } 
    }
], function(err, data){
    if (err) throw err;
    console.log(data);
})

Für mehrere Datumsfilterparameter der $redact Operator wäre

{
    "$redact": {
        "$cond": [
            {
                "$and": [
                     { "$eq": [{ "$year": "$date" },  parseInt(req.params.year)  ]},
                     { "$eq": [{ "$month": "$date" }, parseInt(req.params.month) ]},
                     { "$eq": [{ "$week": "$date" },  parseInt(req.params.week)  ]}
                ]
            },
            "$$KEEP",
            "$$PRUNE"
        ]
    }
}