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

Summe der Filialdokumente in Mongoose

Verwenden von aggregate() Funktion können Sie die folgende Pipeline ausführen, die $sum Operator, um die gewünschten Ergebnisse zu erhalten:

const results = await Cart.aggregate([
    { "$addFields": {
        "totalPrice": {
            "$sum": "$products.subTotal"
        }
    } },
]);

console.log(JSON.stringify(results, null, 4));

und die entsprechende Update-Operation folgt:

db.carts.updateMany(
   { },
   [
        { "$set": {
            "totalPrice": {
                "$sum": "$products.subTotal"
            }
        } },
    ]
)

Oder wenn Sie MongoDB 3.2 und frühere Versionen verwenden, wobei $sum nur in der $Gruppenphase verfügbar ist, können Sie dies tun

const pipeline = [
    { "$unwind": "$products" },
    {
        "$group": {
            "_id": "$_id",
            "products": { "$push": "$products" },
            "userPurchased": { "$first": "$userPurchased" },
            "totalPrice": { "$sum": "$products.subTotal" }
        }
    }
]

Cart.aggregate(pipeline)
    .exec(function(err, results){
        if (err) throw err;
        console.log(JSON.stringify(results, null, 4));
    })

In der obigen Pipeline ist der erste Schritt der $unwind Betreiber

{ "$unwind": "$products" }

was sehr praktisch ist, wenn die Daten als Array gespeichert werden. Wenn der Abwicklungsoperator auf ein Listendatenfeld angewendet wird, generiert er einen neuen Datensatz für jedes einzelne Element des Listendatenfelds, auf das die Abwicklung angewendet wird. Es flacht die Daten im Grunde ab.

Dies ist ein notwendiger Vorgang für die nächste Pipeline-Phase, den $group Schritt, in dem Sie die reduzierten Dokumente nach _id gruppieren Feld, wodurch die denormalisierten Dokumente effektiv wieder in ihr ursprüngliches Schema zurückgruppiert werden.

Der $group Der Pipeline-Operator ähnelt dem SQL-Operator GROUP BY Klausel. In SQL können Sie GROUP BY nicht verwenden es sei denn, Sie verwenden eine der Aggregationsfunktionen. Auf die gleiche Weise müssen Sie auch eine Aggregationsfunktion in MongoDB (genannt Akkumulatoren) verwenden. Mehr über die Akkumulatoren können Sie hier lesen .

In diesem $group Operation, die Logik zur Berechnung des totalPrice und die Rückgabe der ursprünglichen Felder erfolgt über die Akkumulatoren . Sie erhalten dentotalPrice durch Aufsummieren jeder einzelnen subTotal Werte pro Gruppe mit $sum als:

"totalPrice": { "$sum": "$products.subTotal }

Der andere Ausdruck

"userPurchased": { "$first": "$userPurchased" },

gibt ein userPurchased zurück Wert aus dem ersten Dokument für jede Gruppe mit $first . Somit wird das ursprüngliche Dokumentschema vor dem effektiv neu erstellt $unwind

Eine Sache, die hier zu beachten ist, ist, dass MongoDB beim Ausführen einer Pipeline Operatoren ineinander leitet. "Pipe" hat hier die Linux-Bedeutung:Die Ausgabe eines Operators wird zur Eingabe des folgenden Operators. Das Ergebnis jedes Operators ist eine neue Sammlung von Dokumenten. Mongo führt also die obige Pipeline wie folgt aus:

collection | $unwind | $group => result

Als Nebenbemerkung:Um die Pipeline besser zu verstehen oder sie zu debuggen, falls Sie unerwartete Ergebnisse erhalten, führen Sie die Aggregation nur mit dem ersten Pipelineoperator aus. Führen Sie die Aggregation beispielsweise in der Mongo-Shell wie folgt aus:

db.cart.aggregate([
    { "$unwind": "$products" }
])

Überprüfen Sie das Ergebnis, um zu sehen, ob die products Das Array wird ordnungsgemäß dekonstruiert. Wenn das das erwartete Ergebnis liefert, fügen Sie das nächste hinzu:

db.cart.aggregate([
    { "$unwind": "$products" },
    {
        "$group": {
            "_id": "$_id",
            "products": { "$push": "$products" },
            "userPurchased": { "$first": "$userPurchased" },
            "totalPrice": { "$sum": "$products.subTotal" }
        }
    }
])

Wiederholen Sie die Schritte, bis Sie zum letzten Pipeline-Schritt gelangen.

Wenn Sie das Feld aktualisieren möchten, können Sie $out Pipeline-Phase als letzter Schritt. Dadurch werden die resultierenden Dokumente der Aggregationspipeline in dieselbe Sammlung geschrieben, wodurch die Sammlung technisch aktualisiert wird.

var pipeline = [
    { "$unwind": "$products" },
    {
        "$group": {
            "_id": "$_id",
            "products": { "$push": "$products" },
            "userPurchased": { "$first": "$userPurchased" },
            "totalPrice": { "$sum": "$products.subTotal" }
        }
    },
    { "$out": "cart" } // write the results to the same underlying mongo collection
]

AKTUALISIEREN

Um sowohl die Aktualisierung als auch die Abfrage durchzuführen, könnten Sie dann ein find() ausgeben Rufen Sie den aggregierten Callback auf, um den aktualisierten json zu erhalten, d. h.

Cart.aggregate(pipeline)
    .exec(function(err, results){
        if (err) throw err;
        Cart.find().exec(function(err, docs){
            if (err) return handleError(err);
            console.log(JSON.stringify(docs, null, 4));
        })
    })
    

Mit Promises können Sie dies alternativ auch als

tun
Cart.aggregate(pipeline).exec().then(function(res)
    return Cart.find().exec();
).then(function(docs){  
    console.log(JSON.stringify(docs, null, 4));
});