Korzystanie z aggregate()
funkcji, możesz uruchomić następujący potok, który używa $sum
operatora, aby uzyskać pożądane wyniki:
const results = await Cart.aggregate([
{ "$addFields": {
"totalPrice": {
"$sum": "$products.subTotal"
}
} },
]);
console.log(JSON.stringify(results, null, 4));
a odpowiednia operacja aktualizacji następuje:
db.carts.updateMany(
{ },
[
{ "$set": {
"totalPrice": {
"$sum": "$products.subTotal"
}
} },
]
)
Lub jeśli używasz MongoDB 3.2 i wcześniejszych wersji, gdzie $sum
jest dostępny tylko w fazie $group, możesz to zrobić
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));
})
W powyższym potoku pierwszym krokiem jest $odpręż
operator
{ "$unwind": "$products" }
co jest bardzo przydatne, gdy dane są przechowywane w postaci tablicy. Gdy operator unwind zostanie zastosowany do pola danych listy, wygeneruje nowy rekord dla każdego elementu pola danych listy, do którego zastosowano unwind. Zasadniczo spłaszcza dane.
Jest to niezbędna operacja na następnym etapie potoku, $grupa
krok, w którym grupujesz spłaszczone dokumenty według _id
pole, w ten sposób skutecznie przegrupowując zdenormalizowane dokumenty z powrotem do ich oryginalnego schematu.
$group
Operator potoku jest podobny do GROUP BY
w SQL klauzula. W SQL nie możesz używać GROUP BY
chyba że korzystasz z którejkolwiek z funkcji agregacji. W ten sam sposób musisz również użyć funkcji agregacji w MongoDB (zwanej akumulatorami). Więcej informacji o akumulatorach znajdziesz tutaj
.
W tym $group
operacja, logika obliczania całkowitej ceny
a zwrócenie oryginalnych pól odbywa się za pomocą akumulatorów . Otrzymujeszcałkowitą cenę
sumując każdą pojedynczą subTotal
wartości na grupę z $sum
jako:
"totalPrice": { "$sum": "$products.subTotal }
Drugie wyrażenie
"userPurchased": { "$first": "$userPurchased" },
zwróci zakupiony przez użytkownika
wartość z pierwszego dokumentu dla każdej grupy przy użyciu $first
. W ten sposób skutecznie odbudowuje oryginalny schemat dokumentu przed $odpręż
Należy zauważyć, że podczas wykonywania potoku MongoDB łączy ze sobą operatorów. "Pipe" ma tutaj znaczenie Linuksa:wyjście operatora staje się wejściem następującego operatora. Rezultatem każdego operatora jest nowy zbiór dokumentów. Tak więc Mongo wykonuje powyższy potok w następujący sposób:
collection | $unwind | $group => result
Na marginesie, aby pomóc w zrozumieniu potoku lub debugowaniu go w przypadku uzyskania nieoczekiwanych wyników, uruchom agregację tylko z pierwszym operatorem potoku. Na przykład uruchom agregację w powłoce mongo jako:
db.cart.aggregate([
{ "$unwind": "$products" }
])
Sprawdź wynik, aby zobaczyć, czy produkty
tablica jest poprawnie dekonstruowana. Jeśli daje to oczekiwany wynik, dodaj następny:
db.cart.aggregate([
{ "$unwind": "$products" },
{
"$group": {
"_id": "$_id",
"products": { "$push": "$products" },
"userPurchased": { "$first": "$userPurchased" },
"totalPrice": { "$sum": "$products.subTotal" }
}
}
])
Powtarzaj kroki, aż dojdziesz do ostatniego etapu potoku.
Jeśli chcesz zaktualizować pole, możesz dodać $out
etap rurociągu jako ostatni krok. Spowoduje to zapisanie wynikowych dokumentów potoku agregacji do tej samej kolekcji, a tym samym techniczną aktualizację kolekcji.
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
]
AKTUALIZUJ
Aby wykonać zarówno aktualizację, jak i zapytanie, możesz następnie wydać find()
zadzwoń w zagregowanym wywołaniu zwrotnym, aby uzyskać zaktualizowany json, tj.
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));
})
})
Korzystając z Promises, możesz to zrobić alternatywnie jako
Cart.aggregate(pipeline).exec().then(function(res)
return Cart.find().exec();
).then(function(docs){
console.log(JSON.stringify(docs, null, 4));
});