17 tháng 2 năm 2023 | Công nghệ thông tin
1. Khái niệm về dòng xử lý nhóm (Aggregation Pipeline)
Dòng xử lý nhóm trong MongoDB được sử dụng để xử lý tài liệu, bao gồm một hoặc nhiều giai đoạn (Stage). Mỗi giai đoạn thực hiện một loại thao tác trên tài liệu, và tài liệu đầu ra từ một giai đoạn sẽ được truyền đến giai đoạn tiếp theo. Sử dụng dòng xử lý nhóm, bạn có thể lọc, phân nhóm và tính toán các giá trị tổng hợp như trung bình, giá trị lớn nhất, nhỏ nhất hoặc tổng số.
Ngoài việc thực hiện các truy vấn tổng hợp, kể từ phiên bản MongoDB 4.2, dòng xử lý nhóm cũng có thể được sử dụng để cập nhật tài liệu.
Lưu ý: Khi chạy dòng xử lý nhóm bằng phương thức db.collection.aggregate()
, trừ khi dòng này chứa giai đoạn $merge
hoặc $out
, nó sẽ không thay đổi các tài liệu trong tập hợp.
2. Chuẩn bị dữ liệu
Việc so sánh với SQL sẽ giúp hiểu rõ hơn về cách sử dụng các thao tác nhóm trong MongoDB. Phần này sẽ chuẩn bị dữ liệu để thuận tiện cho việc học hỏi qua so sánh.
Xem xét rằng chúng ta có một bảng phones
lưu trữ thông tin điện thoại di động, với các trường sau: id
(khóa chính), name
(tên), type
(loại), price
(giá), quantity
(số lượng) và published_at
(thời gian phát hành).
Câu lệnh tạo bảng phones
như sau:
CREATE TABLE phones (
id SERIAL, -- khóa chính
name VARCHAR(100), -- tên
type VARCHAR(10), -- loại (standard hoặc plus)
price INT, -- giá
quantity INT, -- số lượng
published_at TIMESTAMP, -- thời gian phát hành
PRIMARY KEY (id)
);
Chèn 10 dòng dữ liệu vào bảng phones
với câu lệnh sau:
INSERT INTO phones (name, type, price, quantity, published_at) VALUES
('Apple', 'plus', 7000, 10, '2023-01-16 16:08:00'),
('Apple', 'standard', 6000, 10, '2023-01-16 16:08:00'),
('XIAOMI', 'plus', 3000, 30, '2023-02-16 16:08:00'),
('XIAOMI', 'standard', 2000, 30, '2023-02-16 16:08:00'),
('OPPO', 'plus', 2000, 20, '2023-03-16 16:08:00'),
('OPPO', 'standard', 1000, 20, '2023-03-16 16:08:00'),
('HUAWEI', 'plus', 5000, 40, '2023-04-16 16:08:00'),
('HUAWEI', 'standard', 4000, 40, '2023-04-16 16:08:00'),
('VIVO', 'plus', 3000, 50, '2023-05-16 16:08:00'),
('VIVO', 'standard', 2000, 50, '2023-05-16 16:08:00');
Để chèn cùng dữ liệu này vào MongoDB sử dụng MongoShell, dùng câu lệnh sau:
db.phones.insertMany([
{ _id: 1, name: "Apple", type: "plus", price: 7000,
quantity: 10, published_at: ISODate("2023-01-16T16:08:00Z") },
{ _id: 2, name: "Apple", type: "standard", price: 6000,
quantity: 10, published_at: ISODate("2023-01-16T16:08:00Z") },
{ _id: 3, name: "XIAOMI", type: "plus", price: 3000,
quantity: 30, published_at: ISODate("2023-02-16T16:08:00Z") },
{ _id: 4, name: "XIAOMI", type: "standard", price: 2000,
quantity: 30, published_at: ISODate("2023-02-16T16:08:00Z") },
{ _id: 5, name: "OPPO", type: "plus", price: 2000,
quantity: 20, published_at: ISODate("2023-03-16T16:08:00Z") },
{ _id: 6, name: "OPPO", type: "standard", price: 1000,
quantity: 20, published_at: ISODate("2023-03-16T16:08:00Z") },
{ _id: 7, name: "HUAWEI", type: "plus", price: 5000,
quantity: 40, published_at: ISODate("2023-04-16T16:08:00Z") },
{ _id: 8, name: "HUAWEI", type: "standard", price: 4000,
quantity: 40, published_at: ISODate("2023-04-16T16:08:00Z") },
{ _id: 9, name: "VIVO", type: "plus", price: 3000,
quantity: 50, published_at: ISODate("2023-05-16T16:08:00Z") },
{ _id: 10, name: "VIVO", type: "standard", price: 2000,
quantity: 50, published_at: ISODate("2023-05-16T16:08:00Z") }
]);
Sau khi chuẩn bị xong dữ liệu, phần dưới đây sẽ sử dụng cách tiếp cận so sánh SQL để học về kiến thức dòng xử lý nhóm của MongoDB.
3. So sánh SQL để học cách sử dụng dòng xử lý nhóm
Phần này đặt ra các tình huống vấn đề cụ thể, sau đó giải quyết bằng cả SQL và dòng xử lý nhóm.
3.1 Lọc theo trường, sau đó phân nhóm và sắp xếp
Mô tả vấn đề: Tìm tất cả các điện thoại có loại là standard
, sau đó phân nhóm theo tên và tính tổng số lượng tương ứng, trả về kết quả bao gồm hai cột là tên và tổng số lượng, sắp xếp theo tổng số lượng giảm dần.
Trong SQL, trước tiên chúng ta sử dụng WHERE
để lọc, sau đó dùng GROUP BY
để phân nhóm, sử dụng hàm tổng hợp SUM
để cộng dồn, và cuối cùng dùng ORDER BY
để sắp xếp.
Câu lệnh SQL và kết quả chạy như sau:
SELECT name, SUM(quantity) AS total_quantity FROM phones WHERE type='standard' GROUP BY name ORDER BY [j88vip](https://www.xaxbs.com) total_quantity DESC;
name | total_quantity
--------+----------------
VIVO | 50
HUAWEI | 40
XIAOMI | 30
OPPO | 20
Apple | 10
Để giải quyết vấn đề này bằng dòng xử lý nhóm của MongoDB, cần ba giai đoạn:
- Giai đoạn đầu tiên
$match
: Lọc các tài liệu có loại làstandard
và chuyển kết quả sang giai đoạn tiếp theo. - Giai đoạn thứ hai
$group
: Phân nhóm các tài liệu đầu vào theo tên và tính toán giá trị mới cho trườngtotalQuantity
, đại diện cho tổng số lượng. Sau khi hoàn thành, chuyển kết quả sang giai đoạn tiếp theo. - Giai đoạn thứ ba
$sort
: Sắp xếp các tài liệu đầu vào theototalQuantity
giảm dần và trả về kết quả.
Câu lệnh aggregate
trong MongoShell và kết quả chạy như sau:
db.phones.aggregate([
{ $match: { type: "standard" } },
{ $group: { _id: "$name", totalQuantity: { $sum: "$quantity" } } },
{ $sort: { totalQuantity: -1 } }
]);
[
{ _id: 'VIVO', totalQuantity: 50 },
{ _id: 'HUAWEI', totalQuantity: 40 },
{ _id: 'XIAOMI', totalQuantity: 30 },
{ _id: 'OPPO', totalQuantity: 20 },
{ _id: 'Apple', totalQuantity: 10 }
]
Như vậy, kết quả truy vấn bằng dòng xử lý nhóm của MongoDB giống với kết quả truy vấn bằng SQL ở trên.
3.2 Giới hạn phạm vi thời gian, sau đó phân nhóm và sắp xếp
Mô tả vấn đề: Tìm tất cả các điện thoại được phát hành trong khoảng thời gian từ tháng 2 đến tháng 4 năm 2023, sau đó tính tổng số lượng điện thoại được phát hành theo từng tháng, trả về kết quả bao gồm hai cột là tháng phát hành và tổng số lượng, sắp xếp theo tổng số lượng giảm dần.
Trong SQL, trước tiên chúng ta chuyển đổi dấu thời gian thành định dạng năm-tháng, sau đó dùng WHERE
để giới hạn phạm vi ngày, dùng GROUP BY
để phân nhóm, sử dụng hàm tổng hợp SUM
để cộng dồn, và cuối cùng dùng ORDER BY
để sắp xếp.
Câu lệnh SQL và kết quả chạy như sau:
SELECT TO_CHAR(published_at, 'YYYY-MM') AS year_month, SUM(quantity) AS total_quantity FROM phones WHERE published_at BETWEEN '2023-02-01 00:00:00' AND '2023-05-01 00:00:00' GROUP BY year_month ORDER BY year_month DESC;
year_month | total_quantity
------------+----------------
2023-04 | 80
2023-03 | 40
2023-02 | 60
Để giải quyết vấn đề này bằng dòng xử lý nhóm của MongoDB, cũng cần ba giai đoạn:
- Giai đoạn đầu tiên
$match
: Lọc các tài liệu có ngày phát hành nằm trong khoảng2023-02-01 00:00:00
và2023-05-01 00:00:00
và chuyển kết quả sang giai đoạn tiếp theo. - Giai đoạn thứ hai
$group
: Chuyển đổi ngày phát hành thành định dạng%Y-%m
và phân nhóm theo đó, sau đó tính tổng số lượng điện thoại được phát hành trong tháng đó (totalQuantity
). Sau khi hoàn thành, chuyển kết quả sang giai đoạn tiếp theo. - Giai đoạn thứ ba
$sort
: Sắp xếp các tài liệu đầu vào theo trường tháng-năm giảm dần và trả về kết quả.
Câu lệnh aggregate
trong MongoShell và kết quả chạy như sau:
db.phones.aggregate([
{
$match: {
published_at: {
$gte: new ISODate("2023-02-01 00:00:00"),
$lt: new ISODate("2023-05-01 00:00:00")
}
}
},
{
$group: {
_id: {
$dateToString: {
format: "%Y-%m",
date: "$published_at"
}
},
totalQuantity: { $sum: "$quantity" }
}
},
{
$sort: { _id: -1 }
}
]);
[
{ _id: '2023-04', totalQuantity: 80 },
{ _id: '2023-03', totalQuantity: 40 },
{ _id: '2023-02', totalQuantity: 60 }
]
Như vậy, kết quả truy vấn bằng dòng xử lý nhóm của MongoDB cũng khớp với kết quả truy vấn bằng SQL.
Kết luận, bài viết j88bet đã so sánh SQL để học những thao tác cơ bản nhất của dòng xử lý nhóm trong MongoDB. Các đặc điểm phức tạp hơn nổ hũ 28 của thao tác này sẽ được tìm hiểu và tổ chức sau khi có thêm thời gian.
[1] Thao tác tổng hợp MongoDB - www.mongodb.com [2] Cuốn sách Thực hành Tổng hợp MongoDB - www.practical-mongodb-aggregations.com [3] Định dạng Câu lệnh SQL Trực tuyến - sqlformat.org