Utilisation de aggregate()
fonction, vous pouvez exécuter le pipeline suivant qui utilise le $sum
opérateur pour obtenir les résultats souhaités :
const results = await Cart.aggregate([
{ "$addFields": {
"totalPrice": {
"$sum": "$products.subTotal"
}
} },
]);
console.log(JSON.stringify(results, null, 4));
et l'opération de mise à jour correspondante suit :
db.carts.updateMany(
{ },
[
{ "$set": {
"totalPrice": {
"$sum": "$products.subTotal"
}
} },
]
)
Ou si vous utilisez MongoDB 3.2 et des versions antérieures, où $sum
n'est disponible qu'en $étape de groupe, vous pouvez le faire
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));
})
Dans le pipeline ci-dessus, la première étape est le $unwind
opérateur
{ "$unwind": "$products" }
ce qui est très pratique lorsque les données sont stockées sous forme de tableau. Lorsque l'opérateur de déroulement est appliqué sur un champ de données de liste, il génère un nouvel enregistrement pour chaque élément du champ de données de liste sur lequel le déroulement est appliqué. Il aplatit essentiellement les données.
Il s'agit d'une opération nécessaire pour la prochaine étape du pipeline, le $group
étape où vous regroupez les documents aplatis par le _id
champ, regroupant ainsi efficacement les documents dénormalisés dans leur schéma d'origine.
Le $group
l'opérateur de pipeline est similaire au GROUP BY
du SQL clause. En SQL, vous ne pouvez pas utiliser GROUP BY
sauf si vous utilisez l'une des fonctions d'agrégation. De la même manière, vous devez également utiliser une fonction d'agrégation dans MongoDB (appelée accumulateurs). Vous pouvez en savoir plus sur les accumulateurs ici
.
Dans ce $group
opération, la logique pour calculer le totalPrice
et le retour des champs d'origine se fait via les accumulateurs . Vous obtenez le totalPrice
en additionnant chaque subTotal
individuel valeurs par groupe avec $sum
comme :
"totalPrice": { "$sum": "$products.subTotal }
L'autre expression
"userPurchased": { "$first": "$userPurchased" },
renverra un userPurchased
valeur du premier document pour chaque groupe en utilisant $first
. Reconstruisant ainsi efficacement le schéma du document d'origine avant le $unwind
Une chose à noter ici est que lors de l'exécution d'un pipeline, MongoDB relie les opérateurs les uns aux autres. "Pipe" prend ici le sens Linux :la sortie d'un opérateur devient l'entrée de l'opérateur suivant. Le résultat de chaque opérateur est une nouvelle collection de documents. Mongo exécute donc le pipeline ci-dessus comme suit :
collection | $unwind | $group => result
En remarque, pour vous aider à comprendre le pipeline ou pour le déboguer si vous obtenez des résultats inattendus, exécutez l'agrégation avec uniquement le premier opérateur de pipeline. Par exemple, exécutez l'agrégation dans mongo shell en tant que :
db.cart.aggregate([
{ "$unwind": "$products" }
])
Vérifiez le résultat pour voir si les products
tableau est déconstruit correctement. Si cela donne le résultat attendu, ajoutez le suivant :
db.cart.aggregate([
{ "$unwind": "$products" },
{
"$group": {
"_id": "$_id",
"products": { "$push": "$products" },
"userPurchased": { "$first": "$userPurchased" },
"totalPrice": { "$sum": "$products.subTotal" }
}
}
])
Répétez les étapes jusqu'à ce que vous arriviez à l'étape finale du pipeline.
Si vous souhaitez mettre à jour le champ, vous pouvez ajouter le $out
l'étape du pipeline comme dernière étape. Cela écrira les documents résultants du pipeline d'agrégation dans la même collection, mettant ainsi techniquement à jour la collection.
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
]
MISE À JOUR
Pour faire à la fois la mise à jour et la requête, vous pouvez alors émettre un find()
appelez le rappel agrégé pour obtenir le json mis à jour, c'est-à-dire
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));
})
})
En utilisant Promises, vous pouvez le faire alternativement comme
Cart.aggregate(pipeline).exec().then(function(res)
return Cart.find().exec();
).then(function(docs){
console.log(JSON.stringify(docs, null, 4));
});