Chuỗi MongoDB · Bài 4/6. Sau bài 3 về truy vấn document và mảng, chúng ta chuyển từ tìm từng đơn hàng sang tạo báo cáo từ nhiều đơn.
Một cửa hàng có bốn đơn đã thanh toán. Nhân viên cộng được 1.080.000 đồng tiền hàng, nhưng dashboard lại hiện nhiều hơn. Câu truy vấn không báo lỗi. Vấn đề nằm ở chỗ một đơn có nhiều sản phẩm đã bị tách thành nhiều dòng, rồi tổng tiền của đơn được cộng lại trên từng dòng.
Đó là lý do học Aggregation Pipeline không chỉ là nhớ tên các toán tử. Bạn cần theo dõi mỗi document đang đại diện cho một đơn, một dòng hàng hay một ngày. Hiểu điều này giúp bạn viết báo cáo có thể giải thích và đối chiếu được.
1. Bạn sẽ học được gì và cần chuẩn bị gì?
Bài dành cho người đã biết CRUD, document lồng nhau và mảng. Bạn sẽ thực hành lọc đơn, tính thành tiền, tổng hợp theo ngày Việt Nam, tìm sản phẩm có doanh số cao và phát hiện lỗi đếm lặp. Nếu chưa quen, bắt đầu từ bài 1 về MongoDB và CRUD.
Ví dụ dùng cú pháp tương thích MongoDB 7.0/8.0 và chạy trong mongosh, hoặc tab MongoDB Shell của Compass khi có sẵn. Kết nối tới MongoDB local hoặc Atlas của bạn trước khi chạy. Các đoạn dưới là JavaScript cho shell, không phải JSON thuần để dán nguyên vào từng ô stage của Compass. Kiểm tra phiên bản bằng db.version().
Toàn bộ dữ liệu là giả lập trong database riêng techdata_mongo4_lab. Tiền lưu bằng số nguyên đồng Việt Nam, không có thuế, phí vận chuyển, chiết khấu hoặc hoàn tiền. Vì vậy, chỉ tiêu trong bài là tổng tiền hàng của các đơn paid; đây chưa phải định nghĩa doanh thu kế toán của một doanh nghiệp.
2. Pipeline là gì? Hãy nghĩ như một dây chuyền xử lý
Một pipeline là danh sách các bước xử lý có thứ tự. Mỗi bước gọi là stage. Đầu ra của bước trước trở thành đầu vào của bước sau. Ví dụ, báo cáo theo ngày bắt đầu bằng việc giữ những đơn đã thanh toán, tính tiền từng đơn, gom các đơn có cùng ngày rồi sắp xếp ngày để trình bày [1].
db.orders.aggregate([
{ $match: { status: "paid" } },
{ $project: { _id: 1, customerId: 1, createdAt: 1 } }
])
Dấu ngoặc vuông chứa danh sách stage; mỗi phần tử ở đây có một toán tử stage. $match giữ document phù hợp. $project chọn những trường muốn trả về. Trong ví dụ này, đơn vị dữ liệu vẫn là một đơn hàng. Pipeline chưa gom nhóm hoặc tách mảng.
| Stage | Việc thực hiện | Đơn vị dữ liệu trong ví dụ |
|---|---|---|
$match | Lọc đơn paid trong khoảng thời gian | Một document là một đơn |
$set | Tính thêm orderTotal | Vẫn là một đơn |
$unwind | Tách từng phần tử items | Một dòng hàng của đơn |
$group | Gom ngày hoặc mã sản phẩm | Một nhóm kết quả |
$sort, $limit | Sắp xếp và lấy một số nhóm | Một nhóm kết quả |
$project | Chọn hoặc đổi tên trường đầu ra | Giữ đơn vị của stage trước |
Stage khác expression. Stage là một bước xử lý cả luồng document; expression là công thức dùng bên trong bước đó. $multiply tính tích, $map biến đổi từng phần tử trong mảng. Bạn không đặt $multiply thành một stage độc lập. Còn $sum có thể xuất hiện trong nhiều ngữ cảnh: cộng các giá trị của nhóm hoặc cộng các số trong một mảng [3][5][6].
Các pipeline đọc trong bài không có $merge hoặc $out, nên không ghi kết quả trở lại collection. $set trong aggregate() chỉ thêm trường vào document đang đi qua pipeline; đừng nhầm với $set dùng trong một lệnh cập nhật [1][7].

3. Chuẩn bị dữ liệu mẫu có kết quả đối chiếu
Chạy đoạn sau một lần trong môi trường học. Chúng ta dùng mã đơn cố định để đọc kết quả dễ hơn. Nếu chạy lại insertMany() trên cùng dữ liệu, MongoDB sẽ báo trùng _id; hãy dùng một database lab mới hoặc chỉ xóa collection mẫu khi bạn chủ động muốn làm lại. Không chạy thao tác xóa trên dữ liệu thật.
db = db.getSiblingDB("techdata_mongo4_lab");
db.orders.insertMany([
{
_id: "O401", customerId: "C01", status: "paid",
createdAt: ISODate("2026-10-31T18:30:00Z"),
items: [
{ sku: "P001", qty: 2, unitPrice: 100000 },
{ sku: "P002", qty: 1, unitPrice: 30000 }
]
},
{
_id: "O402", customerId: "C02", status: "paid",
createdAt: ISODate("2026-11-01T03:00:00Z"),
items: [{ sku: "P001", qty: 2, unitPrice: 100000 }]
},
{
_id: "O403", customerId: "C01", status: "paid",
createdAt: ISODate("2026-11-01T18:00:00Z"),
items: [
{ sku: "P002", qty: 5, unitPrice: 30000 },
{ sku: "P003", qty: 1, unitPrice: 200000 }
]
},
{
_id: "O404", customerId: "C03", status: "cancelled",
createdAt: ISODate("2026-11-02T02:00:00Z"),
items: [{ sku: "P003", qty: 1, unitPrice: 200000 }]
},
{
_id: "O405", customerId: "C02", status: "paid",
createdAt: ISODate("2026-11-02T05:00:00Z"),
items: [{ sku: "P001", qty: 3, unitPrice: 100000 }]
}
]);
createdAt là BSON Date, còn qty và unitPrice là số. Giá ở đây là giá chốt trên đơn, phù hợp với nguyên tắc giữ lịch sử đã học ở bài 2. Không lấy giá hiện tại trong danh mục để tính lại đơn cũ.
| Đơn | Ngày tại Việt Nam | Tổng tiền hàng | Đưa vào báo cáo? |
|---|---|---|---|
| O401 | 01/11/2026 | 230.000đ | Có |
| O402 | 01/11/2026 | 200.000đ | Có |
| O403 | 02/11/2026 | 350.000đ | Có |
| O404 | 02/11/2026 | 200.000đ | Không: cancelled |
| O405 | 02/11/2026 | 300.000đ | Có |
Mốc kiểm tra: 5 đơn nguồn, 4 đơn paid, 6 dòng hàng paid, tổng tiền 1.080.000đ. Hãy ghi bốn con số này trước khi viết pipeline. Khi dữ liệu ít, cộng tay là một cách kiểm tra độc lập hữu ích; khi dữ liệu lớn, cần quy trình đối soát với nguồn nghiệp vụ.
4. Thực hành: tính tổng tiền hàng và số đơn theo ngày
4.1. Lọc đúng phạm vi thời gian trước
Tháng 11 tại Việt Nam bắt đầu lúc 00:00 ngày 01/11 theo UTC+7, tương đương 17:00 UTC ngày 31/10. Ta dùng khoảng nửa mở: lấy thời điểm bắt đầu và loại thời điểm kết thúc. Cách này không cần đoán mili giây cuối cùng của tháng, đồng thời tránh chồng lấn giữa hai kỳ liên tiếp.
const paidInNovember = {
status: "paid",
createdAt: {
$gte: ISODate("2026-10-31T17:00:00Z"),
$lt: ISODate("2026-11-30T17:00:00Z")
}
};
db.orders.countDocuments(paidInNovember); // 4
Các phần sau dùng lại biến paidInNovember; giữ cùng phiên mongosh và chạy theo thứ tự. Nếu mở phiên mới, chạy lại đoạn khai báo biến và chọn database lab. Đơn O401 có ngày UTC là 31/10 nhưng phải được tính vào ngày 01/11 ở Việt Nam.

4.2. Tính tổng một đơn mà không tăng số document
const orderTotalStage = {
$set: {
orderTotal: {
$sum: {
$map: {
input: "$items",
as: "item",
in: { $multiply: ["$$item.qty", "$$item.unitPrice"] }
}
}
}
}
};
db.orders.aggregate([
{ $match: paidInNovember },
orderTotalStage,
{ $project: { _id: 1, orderTotal: 1 } },
{ $sort: { _id: 1 } }
]);
Đọc từ trong ra ngoài: $map đi qua items; biến item là phần tử hiện tại nên tham chiếu bằng hai dấu đô la $$item. $multiply tạo thành tiền từng dòng. Với O401, mảng kết quả là [200000, 30000]. $sum cộng mảng thành 230000; $set gắn số đó vào orderTotal [5][6][7].
[
{ _id: "O401", orderTotal: 230000 },
{ _id: "O402", orderTotal: 200000 },
{ _id: "O403", orderTotal: 350000 },
{ _id: "O405", orderTotal: 300000 }
]
Đây là kết quả mong đợi, không phải đoạn lệnh cần chạy. Điểm quan trọng: bốn đơn vẫn tạo bốn document. Cách tính này phù hợp khi báo cáo cần số đơn và tiền theo đơn, không cần trải từng dòng sản phẩm ra ngoài. Nó không thay thế việc kiểm soát schema đầu vào.
4.3. Gom ngày, đếm đơn và định dạng đầu ra
const dailyPipeline = [
{ $match: paidInNovember },
orderTotalStage,
{
$group: {
_id: {
$dateToString: {
date: "$createdAt",
format: "%Y-%m-%d",
timezone: "Asia/Ho_Chi_Minh"
}
},
orderCount: { $sum: 1 },
revenue: { $sum: "$orderTotal" },
avgOrderValue: { $avg: "$orderTotal" }
}
},
{ $sort: { _id: 1 } },
{
$project: {
_id: 0,
day: "$_id",
orderCount: 1,
revenue: 1,
avgOrderValue: 1
}
}
];
db.orders.aggregate(dailyPipeline);
_id bên trong $group là khóa nhóm: mọi đơn có cùng chuỗi ngày được gom chung. Nó không còn là mã đơn gốc. $sum: 1 cộng một cho mỗi document đầu vào của nhóm, còn $sum: "$orderTotal" cộng giá trị tiền. Sau bước này, mỗi document là một ngày [3].
$dateToString dùng múi giờ được chỉ định để tạo nhãn ngày. Nếu bỏ timezone, phép định dạng mặc định theo UTC [4]. $sort sắp ngày vì $group không bảo đảm thứ tự. Cuối cùng $project đổi tên khóa nhóm thành day cho người đọc dễ hiểu [3][8][10].
| day | orderCount | revenue (đồng) | avgOrderValue (đồng) |
|---|---|---|---|
| 2026-11-01 | 2 | 430.000 | 215.000 |
| 2026-11-02 | 2 | 650.000 | 325.000 |
Đối chiếu: 430.000 + 650.000 = 1.080.000; 2 + 2 = 4 đơn. Giá trị trung bình một đơn của cả kỳ là 1.080.000 / 4 = 270.000đ. Không nên mặc định lấy trung bình của các số trung bình theo ngày: cách đó chỉ trùng trong bộ mẫu này vì hai ngày đều có hai đơn. Với số đơn khác nhau, cần chia tổng tiền toàn kỳ cho tổng số đơn.
5. Thực hành: sản phẩm nào tạo nhiều tiền hàng nhất?
Bây giờ câu hỏi thay đổi. Ta cần tính theo sản phẩm nằm trong mảng items. Một document cấp đơn chứa nhiều SKU, nên dùng $unwind để tạo một document cho mỗi phần tử mảng. Đơn O401 tạo hai dòng, nhưng dòng có qty: 2 vẫn chỉ là một dòng hàng, không tự tách thành hai đơn vị sản phẩm [2].
const productPipeline = [
{ $match: paidInNovember },
{ $unwind: "$items" },
{
$group: {
_id: "$items.sku",
units: { $sum: "$items.qty" },
lineCount: { $sum: 1 },
revenue: {
$sum: { $multiply: ["$items.qty", "$items.unitPrice"] }
}
}
},
{ $sort: { revenue: -1, _id: 1 } },
{ $limit: 3 },
{ $project: { _id: 0, sku: "$_id", units: 1, lineCount: 1, revenue: 1 } }
];
db.orders.aggregate(productPipeline);
| SKU | Số lượng bán | Số dòng hàng | Tiền hàng |
|---|---|---|---|
| P001 | 7 | 3 | 700.000đ |
| P003 | 1 | 1 | 200.000đ |
| P002 | 6 | 2 | 180.000đ |
P001 có ba dòng hàng nhưng bán bảy đơn vị. Hai đại lượng trả lời hai câu hỏi khác nhau. Nếu muốn xếp hạng theo lượng bán, đổi tiêu chí sắp xếp thành { units: -1, _id: 1 }; khi đó P002 đứng trước P003. Doanh số cao và số lượng bán cao không nhất thiết cùng một sản phẩm.
Khóa phụ _id: 1 giúp thứ tự ổn định khi hai nhóm có cùng revenue. Đặt $limit sau $sort mới mang nghĩa lấy nhóm đứng đầu. Nếu giới hạn đơn trước khi gom, bạn đang báo cáo trên một phần dữ liệu, không phải tìm top sản phẩm của toàn kỳ [10].
6. Bẫy lớn: cộng tổng tiền đơn sau $unwind

Hãy xem ví dụ cố tình viết sai dưới đây. Mục tiêu là kiểm chứng lỗi, không dùng pipeline này cho báo cáo thật. Ta tính tổng mỗi đơn trước, tách mảng rồi cộng tổng đơn trên từng dòng.
db.orders.aggregate([
{ $match: paidInNovember },
orderTotalStage,
{ $unwind: "$items" },
{
$group: {
_id: null,
documentCount: { $sum: 1 },
wrongRevenue: { $sum: "$orderTotal" }
}
}
]);
// Mong đợi: documentCount = 6, wrongRevenue = 1660000
O401 có hai dòng nên 230.000đ bị cộng hai lần; O403 cũng có hai dòng nên 350.000đ bị cộng hai lần. Tổng sai là 460.000 + 200.000 + 700.000 + 300.000 = 1.660.000đ. MongoDB đã thực hiện đúng câu lệnh; chính cách đặt câu hỏi của chúng ta sai.
Cách sửa tùy mục tiêu: nếu cần chỉ tiêu cấp đơn, giữ pipeline ở cấp đơn như báo cáo ngày. Nếu cần chỉ tiêu cấp dòng hàng, cộng qty × unitPrice của từng dòng. Nếu buộc phải tách rồi cần đếm số đơn có mặt, gom lại theo mã đơn trước khi đếm:
db.orders.aggregate([
{ $match: paidInNovember },
{ $unwind: "$items" },
{ $group: { _id: "$_id" } },
{ $count: "distinctOrderCount" }
]);
// [{ distinctOrderCount: 4 }]
Cách đếm này chỉ tính đơn còn hiện diện sau các stage trước đó. Theo mặc định, $unwind không tạo đầu ra cho mảng rỗng, trường thiếu hoặc null. Nếu dùng preserveNullAndEmptyArrays: true, các đơn ấy được giữ lại, nhưng công thức tiền phải xử lý dữ liệu thiếu theo chính sách nghiệp vụ [2]. Không tự biến dữ liệu lỗi thành số 0 rồi coi báo cáo đã đúng.
7. Khi nào cần $lookup?
Báo cáo tiền hàng ở trên không cần nối collection vì mã sản phẩm và giá chốt đã nằm trên đơn. Nếu cần bổ sung tên danh mục hiện tại hoặc thông tin quản lý hiện tại, bạn có thể dùng $lookup. Toán tử này bổ sung một mảng document khớp từ collection khác; nó không tự tạo ra đúng một document duy nhất [11].
db.products.insertMany([
{ _id: "P001", name: "Bình giữ nhiệt" },
{ _id: "P002", name: "Sổ tay" },
{ _id: "P003", name: "Đèn bàn" }
]);
db.orders.aggregate([
{ $match: { _id: "O401" } },
{ $unwind: "$items" },
{
$lookup: {
from: "products",
localField: "items.sku",
foreignField: "_id",
as: "productInfo"
}
},
{ $project: { _id: 0, sku: "$items.sku", productInfo: 1 } }
]);
Kết quả có hai document cho O401; mỗi document chứa một mảng productInfo có một sản phẩm tương ứng. Ví dụ này nối vào products._id, một khóa duy nhất. Nếu nối bằng trường không duy nhất rồi tiếp tục unwind kết quả, số dòng có thể lại tăng. Hãy kiểm tra quan hệ một-một hay một-nhiều trước khi cộng tiền.
Tên lấy qua lookup ở đây là tên hiện tại. Nếu cần in lại hóa đơn đúng thời điểm mua, lưu snapshot tên trên đơn. Hiệu năng lookup phụ thuộc điều kiện nối, index và lượng dữ liệu, nên không có quy tắc “cứ lookup là chậm” hoặc “MongoDB không cần join” cho mọi hệ thống [11].
8. Lỗi thường gặp: kiểm tra bằng dấu hiệu cụ thể
| Dấu hiệu | Nguyên nhân nên kiểm tra | Cách xử lý |
|---|---|---|
| Thiếu O401 trong tháng 11 | Lọc ngày đầu tháng theo UTC | Dùng cận UTC tương ứng giờ Việt Nam |
| Tổng tiền cao bất thường | Cộng trường cấp đơn sau unwind | Kiểm tra cấp dữ liệu và tính theo dòng hoặc theo đơn |
| Đếm được 6 thay vì 4 | Đếm dòng hàng thành số đơn | Giữ cấp đơn hoặc gom mã đơn trước |
| Tiền thành 0 hoặc phép nhân lỗi | Giá là chuỗi, thiếu trường hoặc sai kiểu | Kiểm tra kiểu dữ liệu, sửa nguồn và validation |
| Không còn customerId sau group | Trường không được đưa vào khóa hoặc accumulator | Xác định lại khóa nhóm; không lấy ngẫu nhiên một khách |
| Kết quả đổi thứ tự giữa các lần chạy | Thiếu sort hoặc khóa phụ khi bằng điểm | Sort rõ ràng và thêm khóa duy nhất của nhóm |
Muốn kiểm tra kiểu giá đang lưu, hãy quan sát đầu ra nhỏ trước khi tổng hợp. Đoạn dưới cho biết MongoDB nhìn thấy kiểu gì; "100000" là chuỗi, không phải số. Việc ứng dụng hiển thị chúng giống nhau không làm kiểu lưu trữ trở nên giống nhau.
db.orders.aggregate([
{ $unwind: "$items" },
{
$project: {
_id: 1,
price: "$items.unitPrice",
priceType: { $type: "$items.unitPrice" },
qtyType: { $type: "$items.qty" }
}
},
{ $limit: 10 }
]);
Với tiền cần phần thập phân hoặc tổng giá trị lớn, thiết kế kiểu số và cách làm tròn ngay từ đầu; cân nhắc Decimal128 hoặc biểu diễn đơn vị tiền nhỏ nhất với miền giá trị phù hợp. Không trộn tùy tiện chuỗi, double và decimal trong cùng chỉ tiêu. Bộ mẫu dùng số nguyên nhỏ để tập trung vào logic pipeline.
Cách debug dễ nhất là chạy từng tiền tố pipeline: trước tiên chỉ match; thêm tính orderTotal; sau đó mới group. Ở mỗi bước, nhìn vài document và đếm số dòng. Với trường tự tính, kiểm tra phép nhân trên một đơn cụ thể. Với nhóm, tự cộng lại một nhóm nhỏ. Bạn sẽ xác định được stage đầu tiên làm kết quả khác kỳ vọng.
9. Khi nào áp dụng và cần chú ý hiệu năng gì?
Aggregation phù hợp với báo cáo vận hành gần dữ liệu nguồn, như tiền hàng theo ngày, lượng bán theo SKU hoặc phân bổ trạng thái đơn. Nếu báo cáo nối nhiều hệ thống, cần lịch sử dài và nhiều người truy vấn nặng cùng lúc, hãy đánh giá mô hình dữ liệu phân tích riêng. Quyết định dựa trên tải thực tế và yêu cầu độ mới dữ liệu, không chỉ số lượng stage.
Đặt bộ lọc trên trường nguồn sớm khi đúng về ngữ nghĩa, để hạn chế dữ liệu đi vào phép tách mảng và gom nhóm. Với bộ lọc bằng status và khoảng createdAt, một ứng viên index là { status: 1, createdAt: 1 }. Đây là điểm bắt đầu để đo, không phải bảo đảm tăng tốc cho mọi phân bố dữ liệu [9][12].
db.orders.createIndex({ status: 1, createdAt: 1 });
db.orders.explain("executionStats").aggregate(dailyPipeline);
Đọc kế hoạch thực thi để xem số document và khóa index đã xét, có quét toàn collection hay dùng index, và có dấu hiệu xử lý tốn tài nguyên không. Năm document lab quá ít để kết luận index hiệu quả; cần đo với dữ liệu đại diện. Index có chi phí dung lượng và cập nhật khi ghi.
Index phục vụ lọc ban đầu không đồng nghĩa sẽ sắp xếp được trường revenue vừa tính sau group. Cũng không cần tự động thêm project ở đầu chỉ để “giảm cột”: MongoDB đã có tối ưu nhận biết các trường cần dùng. Hãy kiểm tra explain thay vì thêm bước theo thói quen [12].
Một số stage như group hoặc sort có thể cần nhiều bộ nhớ. Tùy phiên bản và cấu hình, các stage hỗ trợ có thể ghi dữ liệu tạm ra đĩa khi vượt ngưỡng; allowDiskUse không làm pipeline sai trở thành đúng hoặc luôn nhanh hơn. Mỗi document kết quả vẫn chịu giới hạn BSON 16 MiB. Đừng gom toàn bộ lịch sử thành một mảng khổng lồ chỉ để trả về một document [13].
10. Bài tập tự luyện: kiểm tra bằng số liệu, không chỉ bằng cú pháp
Bài 1: tổng tiền hàng theo khách
Yêu cầu: chỉ lấy đơn trong paidInNovember, trả customerId, số đơn và tổng tiền hàng; sắp tiền giảm dần, thêm mã khách làm khóa phụ. Gợi ý: dùng orderTotalStage, group theo customerId và không unwind.
db.orders.aggregate([
{ $match: paidInNovember },
orderTotalStage,
{ $group: {
_id: "$customerId",
orderCount: { $sum: 1 },
revenue: { $sum: "$orderTotal" }
} },
{ $sort: { revenue: -1, _id: 1 } },
{ $project: { _id: 0, customerId: "$_id", orderCount: 1, revenue: 1 } }
]);
Đáp án: C01 có hai đơn, 580.000đ; C02 có hai đơn, 500.000đ. C03 không xuất hiện vì chỉ có đơn cancelled. Tổng hai khách vẫn phải bằng 1.080.000đ. Nếu tổng khác, kiểm tra bộ lọc hoặc số lần mỗi đơn được cộng.
Bài 2: chỉ giữ ngày có tiền hàng trên 500.000đ
Gợi ý: điều kiện trên revenue cần đặt sau group vì trường này chưa tồn tại ở document nguồn. Chèn { $match: { revenue: { $gt: 500000 } } } ngay sau stage group của dailyPipeline. Đáp án chỉ còn ngày 2026-11-02, hai đơn, 650.000đ. Đặt bộ lọc trên trường tính toán quá sớm có thể làm mất toàn bộ kết quả.
Bài 3: phân biệt tìm đơn có P001 và tính tiền của P001
Yêu cầu A: tính tổng tiền của toàn bộ đơn có chứa P001. Yêu cầu B: chỉ tính tiền của các dòng P001. Hai yêu cầu khác nhau. Gợi ý A: match thêm "items.sku": "P001" ở cấp đơn rồi tính orderTotal. Gợi ý B: unwind, match lại SKU trên dòng đã tách rồi cộng qty × unitPrice.
Đáp án A là 730.000đ từ O401, O402, O405; đáp án B là 700.000đ. Chênh 30.000đ là tiền P002 đi cùng đơn O401. Một bộ lọc mảng ở cấp đơn không tự cắt bỏ các phần tử còn lại của mảng. Đây là cầu nối trực tiếp với cách hiểu mảng ở bài 3.
11. Tổng kết: hiểu đơn vị dữ liệu trước khi chọn toán tử
Một pipeline đúng phải trả lời rõ ba câu hỏi: lấy những đơn nào, mỗi document đại diện cho điều gì, và chỉ tiêu được cộng theo đơn hay dòng hàng. Sau đó mới chọn stage để thể hiện logic ấy.
Trong bài này, báo cáo ngày giữ cấp đơn nên đếm được bốn đơn và tổng tiền 1.080.000đ. Báo cáo sản phẩm chuyển sang sáu dòng hàng, cộng thành tiền từng dòng và vẫn đối chiếu về cùng tổng. Múi giờ được thống nhất ở cả bộ lọc lẫn khóa ngày. Đó là ba kiểm tra bạn có thể áp dụng ngay cho báo cáo thực tế.
Bài 5 tiếp theo: Index và explain trong MongoDB: đọc kế hoạch thực thi, chọn compound index và kiểm tra truy vấn bằng số liệu. Trước đó, hãy tự sửa pipeline cho ba bài tập và giải thích kết quả bằng lời; nếu chỉ chạy ra số mà chưa giải thích được, hãy quay lại xem cấp dữ liệu sau mỗi stage.
12. Tài liệu tham khảo
Nguồn kỹ thuật chính thức của MongoDB, đối chiếu ngày 12/11/2026. Tài liệu nhánh manual có thể thay đổi; khi triển khai, chọn phiên bản khớp server của bạn. Các con số cửa hàng và tình huống trong bài là dữ liệu giảng dạy tự xây dựng.
- Aggregation Operations
- $unwind
- $group
- $dateToString
- $sum · $sum expression
- $map và $multiply · $multiply
- $set
- $project
- $match
- $sort
- $lookup
- Aggregation Pipeline Optimization
- Aggregation Pipeline Limits
Học cùng TechData.AI: Bạn muốn luyện MongoDB trong một project Data Engineering có dữ liệu mẫu, bài tập và hướng dẫn từng bước? Khám phá chương trình đào tạo tại TechData.AI và chọn lộ trình phù hợp với nền tảng hiện tại.

Comments are closed!