Menggunakan aggregate()
fungsi, Anda dapat menjalankan pipa berikut yang menggunakan $sum
operator untuk mendapatkan hasil yang diinginkan:
const results = await Cart.aggregate([
{ "$addFields": {
"totalPrice": {
"$sum": "$products.subTotal"
}
} },
]);
console.log(JSON.stringify(results, null, 4));
dan operasi pembaruan yang sesuai mengikuti:
db.carts.updateMany(
{ },
[
{ "$set": {
"totalPrice": {
"$sum": "$products.subTotal"
}
} },
]
)
Atau jika menggunakan MongoDB 3.2 dan versi sebelumnya, di mana $sum
tersedia di $group stage saja, Anda dapat melakukannya
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));
})
Pada pipeline di atas, langkah pertama adalah $unwind
operator
{ "$unwind": "$products" }
yang sangat berguna ketika data disimpan sebagai array. Ketika operator pelepasan diterapkan pada bidang data daftar, itu akan menghasilkan catatan baru untuk setiap elemen dari bidang data daftar tempat pelepasan diterapkan. Ini pada dasarnya meratakan data.
Ini adalah operasi yang diperlukan untuk tahap pipeline berikutnya, $group
langkah di mana Anda mengelompokkan dokumen yang diratakan dengan _id
bidang, sehingga secara efektif mengelompokkan kembali dokumen yang didenormalisasi kembali ke skema aslinya.
$group
operator pipa mirip dengan GROUP BY
SQL ayat. Dalam SQL, Anda tidak dapat menggunakan GROUP BY
kecuali Anda menggunakan salah satu fungsi agregasi. Dengan cara yang sama, Anda juga harus menggunakan fungsi agregasi di MongoDB (disebut akumulator). Anda dapat membaca lebih lanjut tentang akumulator di sini
.
Dalam $group
operasi, logika untuk menghitung totalPrice
dan mengembalikan kolom asli adalah melalui akumulator . Anda mendapatkantotalHarga
dengan menjumlahkan masing-masing subTotal
nilai per grup dengan $sum
sebagai:
"totalPrice": { "$sum": "$products.subTotal }
Ekspresi lainnya
"userPurchased": { "$first": "$userPurchased" },
akan mengembalikan userPurchased
nilai dari dokumen pertama untuk setiap grup menggunakan $first
. Jadi secara efektif membangun kembali skema dokumen asli sebelum $unwind
Satu hal yang perlu diperhatikan di sini adalah ketika menjalankan pipeline, MongoDB menyalurkan operator ke satu sama lain. "Pipa" di sini mengambil arti Linux:output dari operator menjadi input dari operator berikut. Hasil dari setiap operator adalah kumpulan dokumen baru. Jadi Mongo mengeksekusi pipa di atas sebagai berikut:
collection | $unwind | $group => result
Sebagai catatan tambahan, untuk membantu memahami pipeline atau men-debug-nya jika Anda mendapatkan hasil yang tidak terduga, jalankan agregasi hanya dengan operator pipeline pertama. Misalnya, jalankan agregasi di mongo shell sebagai:
db.cart.aggregate([
{ "$unwind": "$products" }
])
Periksa hasilnya untuk melihat apakah produk
array didekonstruksi dengan benar. Jika itu memberikan hasil yang diharapkan, tambahkan berikutnya:
db.cart.aggregate([
{ "$unwind": "$products" },
{
"$group": {
"_id": "$_id",
"products": { "$push": "$products" },
"userPurchased": { "$first": "$userPurchased" },
"totalPrice": { "$sum": "$products.subTotal" }
}
}
])
Ulangi langkah-langkah tersebut sampai Anda mencapai langkah pipa terakhir.
Jika Anda ingin memperbarui bidang, Anda dapat menambahkan $out
tahap pipa sebagai langkah terakhir. Ini akan menulis dokumen yang dihasilkan dari pipa agregasi ke koleksi yang sama, sehingga secara teknis memperbarui koleksi.
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
]
PERBARUI
Untuk melakukan pembaruan dan kueri, Anda dapat mengeluarkan find()
panggil panggilan balik agregat untuk mendapatkan json yang diperbarui yaitu
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));
})
})
Menggunakan Promises, Anda dapat melakukan ini sebagai alternatif
Cart.aggregate(pipeline).exec().then(function(res)
return Cart.find().exec();
).then(function(docs){
console.log(JSON.stringify(docs, null, 4));
});