Brug af aggregate()
funktion, kan du køre følgende pipeline, som bruger $sum
operatør for at få de ønskede resultater:
const results = await Cart.aggregate([
{ "$addFields": {
"totalPrice": {
"$sum": "$products.subTotal"
}
} },
]);
console.log(JSON.stringify(results, null, 4));
og den tilsvarende opdateringshandling følger:
db.carts.updateMany(
{ },
[
{ "$set": {
"totalPrice": {
"$sum": "$products.subTotal"
}
} },
]
)
Eller hvis du bruger MongoDB 3.2 og tidligere versioner, hvor $sum
er kun tilgængelig i $group-stadiet, kan du gøre
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));
})
I ovenstående pipeline er det første trin $unwind
operatør
{ "$unwind": "$products" }
hvilket er ret praktisk, når dataene er gemt som et array. Når afviklingsoperatoren anvendes på et listedatafelt, genererer den en ny post for hvert element i listedatafeltet, som afvikling er anvendt på. Det flader grundlæggende dataene ud.
Dette er en nødvendig operation for det næste pipelinetrin, $gruppe
trin, hvor du grupperer de fladtrykte dokumenter efter _id
felt og dermed effektivt omgruppere de denormaliserede dokumenter tilbage til deres oprindelige skema.
$group
pipeline-operatoren ligner SQL's GROUP BY
klausul. I SQL kan du ikke bruge GROUP BY
medmindre du bruger nogen af aggregeringsfunktionerne. På samme måde skal du bruge en aggregeringsfunktion i MongoDB (kaldet akkumulatorer). Du kan læse mere om akkumulatorerne her
.
I denne $group
operation, logikken til at beregne totalPrice
og returnering af de originale felter sker gennem akkumulatorerne . Du får totalPrice
ved at opsummere hver enkelt subTotal
værdier pr. gruppe med $sum
som:
"totalPrice": { "$sum": "$products.subTotal }
Det andet udtryk
"userPurchased": { "$first": "$userPurchased" },
returnerer en userPurchased
værdi fra det første dokument for hver gruppe ved hjælp af $first
. Derved genopbygges det originale dokumentskema effektivt før $unwind
En ting at bemærke her er, når man udfører en pipeline, MongoDB piper operatører ind i hinanden. "Rør" har her Linux-betydningen:output fra en operatør bliver input fra følgende operatør. Resultatet af hver operatør er en ny samling af dokumenter. Så Mongo udfører ovenstående pipeline som følger:
collection | $unwind | $group => result
Som en sidebemærkning, for at hjælpe med at forstå pipelinen eller fejlfinde den, hvis du får uventede resultater, skal du køre aggregeringen med kun den første pipeline-operatør. Kør f.eks. aggregeringen i mongo-shell som:
db.cart.aggregate([
{ "$unwind": "$products" }
])
Tjek resultatet for at se, om produkterne
array er dekonstrueret korrekt. Hvis det giver det forventede resultat, skal du tilføje det næste:
db.cart.aggregate([
{ "$unwind": "$products" },
{
"$group": {
"_id": "$_id",
"products": { "$push": "$products" },
"userPurchased": { "$first": "$userPurchased" },
"totalPrice": { "$sum": "$products.subTotal" }
}
}
])
Gentag trinene, indtil du kommer til det sidste pipeline-trin.
Hvis du vil opdatere feltet, kan du tilføje $out
pipeline-fasen som det sidste trin. Dette vil skrive de resulterende dokumenter fra aggregeringspipelinen til den samme samling og dermed teknisk opdatere samlingen.
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
]
OPDATERING
For at udføre både opdateringen og forespørgslen kan du derefter udstede en find()
ring ind det samlede tilbagekald for at få den opdaterede json, dvs.
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));
})
})
Ved at bruge Promises kan du gøre dette alternativt som
Cart.aggregate(pipeline).exec().then(function(res)
return Cart.find().exec();
).then(function(docs){
console.log(JSON.stringify(docs, null, 4));
});