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
tunCart.aggregate(pipeline).exec().then(function(res)
return Cart.find().exec();
).then(function(docs){
console.log(JSON.stringify(docs, null, 4));
});