Sử dụng tổng hợp ()
, bạn có thể chạy đường dẫn sau sử dụng $ sum
toán tử để có được kết quả mong muốn:
const results = await Cart.aggregate([
{ "$addFields": {
"totalPrice": {
"$sum": "$products.subTotal"
}
} },
]);
console.log(JSON.stringify(results, null, 4));
và thao tác cập nhật tương ứng như sau:
db.carts.updateMany(
{ },
[
{ "$set": {
"totalPrice": {
"$sum": "$products.subTotal"
}
} },
]
)
Hoặc nếu sử dụng MongoDB 3.2 và các phiên bản cũ hơn, trong đó $ sum
chỉ khả dụng ở vòng bảng $, bạn có thể thực hiện
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));
})
Trong quy trình trên, bước đầu tiên là $ unwind
nhà điều hành
{ "$unwind": "$products" }
điều này khá tiện dụng khi dữ liệu được lưu trữ dưới dạng mảng. Khi toán tử thư giãn được áp dụng trên trường dữ liệu danh sách, nó sẽ tạo một bản ghi mới cho từng và mọi phần tử của trường dữ liệu danh sách mà thao tác thư giãn được áp dụng. Về cơ bản, nó làm phẳng dữ liệu.
Đây là thao tác cần thiết cho giai đoạn tiếp theo, $ group
bước nơi bạn nhóm các tài liệu được làm phẳng theo _id
, do đó, tập hợp lại một cách hiệu quả các tài liệu không chuẩn hóa trở lại lược đồ ban đầu của chúng.
$ group
toán tử đường ống tương tự như GROUP BY
của SQL mệnh đề. Trong SQL, bạn không thể sử dụng GROUP BY
trừ khi bạn sử dụng bất kỳ hàm tổng hợp nào. Theo cách tương tự, bạn cũng phải sử dụng một hàm tổng hợp trong MongoDB (được gọi là bộ tích lũy). Bạn có thể đọc thêm về bộ tích lũy tại đây
.
Trong $ group này
hoạt động, logic để tính toán totalPrice
và việc trả lại các trường gốc thông qua bộ tích lũy . Bạn nhận được totalPrice
bằng cách cộng gộp từng subTotal
riêng lẻ các giá trị cho mỗi nhóm với $ sum
như:
"totalPrice": { "$sum": "$products.subTotal }
Biểu thức khác
"userPurchased": { "$first": "$userPurchased" },
sẽ trả về một userPurchased
giá trị từ tài liệu đầu tiên cho mỗi nhóm bằng cách sử dụng $ first
. Do đó, việc xây dựng lại một cách hiệu quả giản đồ tài liệu gốc trước $ thư giãn
Một điều cần lưu ý ở đây là khi thực hiện một đường ống, MongoDB đưa các nhà khai thác vào nhau. "Pipe" ở đây mang nghĩa Linux:đầu ra của một toán tử trở thành đầu vào của toán tử sau. Kết quả của mỗi toán tử là một tập hợp tài liệu mới. Vì vậy Mongo thực hiện đường dẫn trên như sau:
Bộ sưu tậpcollection | $unwind | $group => result
Lưu ý thêm, để giúp hiểu đường ống hoặc gỡ lỗi nếu bạn nhận được kết quả không mong muốn, hãy chạy tổng hợp chỉ với nhà điều hành đường ống đầu tiên. Ví dụ:chạy tổng hợp trong mongo shell dưới dạng:
db.cart.aggregate([
{ "$unwind": "$products" }
])
Kiểm tra kết quả để xem liệu các sản phẩm
mảng được giải cấu trúc đúng cách. Nếu điều đó mang lại kết quả mong đợi, hãy thêm phần tiếp theo:
db.cart.aggregate([
{ "$unwind": "$products" },
{
"$group": {
"_id": "$_id",
"products": { "$push": "$products" },
"userPurchased": { "$first": "$userPurchased" },
"totalPrice": { "$sum": "$products.subTotal" }
}
}
])
Lặp lại các bước cho đến khi bạn đến bước cuối cùng.
Nếu bạn muốn cập nhật trường thì bạn có thể thêm $ out
bước cuối cùng. Thao tác này sẽ ghi các tài liệu kết quả của quy trình tổng hợp vào cùng một bộ sưu tập, do đó cập nhật bộ sưu tập về mặt kỹ thuật.
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
]
CẬP NHẬT
Để thực hiện cả cập nhật và truy vấn, bạn có thể phát hành một find ()
gọi lại trong cuộc gọi lại tổng hợp để nhận json cập nhật tức là
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));
})
})
Sử dụng Promises, bạn có thể thực hiện việc này theo cách khác như
Cart.aggregate(pipeline).exec().then(function(res)
return Cart.find().exec();
).then(function(docs){
console.log(JSON.stringify(docs, null, 4));
});