Utilizzo di aggregate()
funzione, è possibile eseguire la pipeline seguente che utilizza $sum
operatore per ottenere i risultati desiderati:
const results = await Cart.aggregate([
{ "$addFields": {
"totalPrice": {
"$sum": "$products.subTotal"
}
} },
]);
console.log(JSON.stringify(results, null, 4));
e la corrispondente operazione di aggiornamento segue:
db.carts.updateMany(
{ },
[
{ "$set": {
"totalPrice": {
"$sum": "$products.subTotal"
}
} },
]
)
O se si utilizza MongoDB 3.2 e versioni precedenti, dove $sum
è disponibile solo nella fase a gironi $, puoi farlo
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));
})
Nella pipeline di cui sopra, il primo passaggio è il $unwind
operatore
{ "$unwind": "$products" }
che è abbastanza utile quando i dati vengono archiviati come un array. Quando l'operatore di rimozione viene applicato a un campo dati elenco, genererà un nuovo record per ogni elemento del campo dati elenco su cui è applicato lo svolgimento. Fondamentalmente appiattisce i dati.
Questa è un'operazione necessaria per la fase successiva della pipeline, il $group
passaggio in cui raggruppi i documenti appiattiti per _id
campo, raggruppando così efficacemente i documenti denormalizzati nel loro schema originale.
Il $group
l'operatore della pipeline è simile a GROUP BY
di SQL clausola. In SQL, non puoi usare GROUP BY
a meno che non si utilizzi una delle funzioni di aggregazione. Allo stesso modo, devi usare anche una funzione di aggregazione in MongoDB (chiamata accumulatori). Puoi leggere ulteriori informazioni sugli accumulatori qui
.
In questo $group
operazione, la logica per calcolare il totalPrice
e la restituzione dei campi originali avviene tramite gli accumulatori . Ottieni il totalPrice
sommando ogni singolo subTotal
valori per gruppo con $sum
come:
"totalPrice": { "$sum": "$products.subTotal }
L'altra espressione
"userPurchased": { "$first": "$userPurchased" },
restituirà un userPurchased
valore dal primo documento per ogni gruppo che utilizza $first
. Ricostruendo così efficacemente lo schema del documento originale prima del $unwind
Una cosa da notare qui è che quando si esegue una pipeline, MongoDB convoglia gli operatori l'uno nell'altro. "Pipe" qui assume il significato di Linux:l'output di un operatore diventa l'input dell'operatore successivo. Il risultato di ogni operatore è una nuova raccolta di documenti. Quindi Mongo esegue la pipeline di cui sopra come segue:
collection | $unwind | $group => result
Come nota a margine, per aiutare a comprendere la pipeline o per eseguirne il debug in caso di risultati imprevisti, eseguire l'aggregazione solo con il primo operatore della pipeline. Ad esempio, esegui l'aggregazione in mongo shell come:
db.cart.aggregate([
{ "$unwind": "$products" }
])
Controlla il risultato per vedere se i products
l'array è decostruito correttamente. Se questo dà il risultato atteso, aggiungi il prossimo:
db.cart.aggregate([
{ "$unwind": "$products" },
{
"$group": {
"_id": "$_id",
"products": { "$push": "$products" },
"userPurchased": { "$first": "$userPurchased" },
"totalPrice": { "$sum": "$products.subTotal" }
}
}
])
Ripeti i passaggi fino ad arrivare al passaggio finale della pipeline.
Se desideri aggiornare il campo, puoi aggiungere il $out
fase della pipeline come ultimo passaggio. Questo scriverà i documenti risultanti della pipeline di aggregazione nella stessa raccolta, aggiornando così tecnicamente la raccolta.
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
]
AGGIORNAMENTO
Per eseguire sia l'aggiornamento che la query, puoi quindi emettere un find()
chiama nella richiamata aggregata per ottenere il json aggiornato, ad esempio
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));
})
})
Usando Promises, puoi farlo in alternativa come
Cart.aggregate(pipeline).exec().then(function(res)
return Cart.find().exec();
).then(function(docs){
console.log(JSON.stringify(docs, null, 4));
});