Aggregation là một framework tổng hợp dữ liệu của MongoDB. Aggregation được xây dựng dựa trên mô hình xử lý dữ liệu dưới dạng pipeline. Aggregation pipeline bao gồm nhiều stage. Trong mỗi stage, chúng ta sử dụng một aggregation operator để biến đổi dữ liệu của các input document. Các output document của stage phía trước sẽ là input document của stage ngay sau. Các aggregation operator có
thể được sử dụng nhiều lần trong pipeline, ngoại trừ Điểm mạnh của aggregation framework là: MongoDB cung cấp phương thức Cú pháp: Trong đó: ... Để minh họa cho các operator, mình sẽ sử dụng 2 collection là Collection Collection Cú pháp: Cú pháp query của Ví dụ: Lọc
các order của customer có ID là A123: => Kết quả: Cú pháp: Trong đó: Ví dụ: => Kết quả: Cú pháp: Trong đó:1. Aggregation overview
$out
, $merge
, và $geoNear
.db.collection.aggregate[]
để chạy aggregation pipeline.db.userCollection.aggregate[ [ { }, { }, ..., { } ], { } ];
orders
và customers
.orders
:/* 1 */
{
"_id" : ObjectId["5dfed89743e6fed50628907c"],
"cust_id" : "A123",
"products" : [
"apple",
"lemon"
],
"amount" : 500,
"status" : "completed"
}
/* 2 */
{
"_id" : ObjectId["5dfed8a643e6fed50628908a"],
"cust_id" : "B456",
"products" : [
"lemon"
],
"amount" : 100,
"status" : "processing"
}
/* 3 */
{
"_id" : ObjectId["5dfed8b043e6fed50628908f"],
"cust_id" : "B456",
"products" : [
"apple",
"orange"
],
"amount" : 300,
"status" : "completed"
}
/* 4 */
{
"_id" : ObjectId["5dfed8b943e6fed506289094"],
"cust_id" : "C789",
"products" : [
"apple",
"lemon",
"orange"
],
"amount" : 800,
"status" : "completed"
}
/* 5 */
{
"_id" : ObjectId["5dfed8c343e6fed506289097"],
"cust_id" : "A123",
"products" : [
"apple"
],
"amount" : 250,
"status" : "completed"
}
customers
:/* 1 */
{
"_id": "A123",
"name": "Alice"
}
/* 2 */
{
"_id": "B456",
"name": "Bob"
}
/* 3 */
{
"_id": "C789",
"name": "Carol"
}
2.
$match
$match
được dùng để lọc các document theo một điều kiện nào đó. $match
tương tự như WHERE
và HAVING
trong SQL.{ $match: { } }
$match
y hệt cú pháp của read operation query [tương tự như find[]
].db.orders.aggregate[[
{
$match: {
cust_id: "A123"
}
}
]]
/* 1 */
{
"_id" : ObjectId["5dfed89743e6fed50628907c"],
"cust_id" : "A123",
"products" : [
"apple",
"lemon"
],
"amount" : 500,
"status" : "completed"
}
/* 2 */
{
"_id" : ObjectId["5dfed8c343e6fed506289097"],
"cust_id" : "A123",
"products" : [
"apple"
],
"amount" : 250,
"status" : "completed"
}
3.
$project
$project
được dùng để chỉ định các field sẽ xuất hiện trong output document. Đó có thể là các field đã tồn tại trong input document, hoặc cũng có thể là các field được tính toán mới. $project
tương tự như SELECT
trong SQL.{ $project: { } }
specification
có thể có các dạng sau:_id:
: field _id
sẽ không xuất hiện trong output document [mặc định
_id
luôn xuất hiện trong output document].:
: field X sẽ xuất hiện trong output document.:
: field X sẽ được tính toán dựa trên một expression nào đó.db.orders.aggregate[[
{
$project: {
_id: 0,
cust_id: 1,
new_amount: { $add: ["$amount", 100] }
}
}
]]
/* 1 */
{
"cust_id" : "A123",
"new_amount" : 600.0
}
/* 2 */
{
"cust_id" : "B456",
"new_amount" : 200.0
}
/* 3 */
{
"cust_id" : "B456",
"new_amount" : 400.0
}
/* 4 */
{
"cust_id" : "C789",
"new_amount" : 900.0
}
/* 5 */
{
"cust_id" : "A123",
"new_amount" : 350.0
}
4.
$count
$count
mới xuất hiện trong MongoDB version 3.4. $count
trả về thêm trong output một field X chứa tổng số input document.{ $count: }
là tên của field X, phải khác rỗng, không
được bắt đầu bằng ký tự
$
và không được bao gồm ký tự .
.
Ví dụ:
db.orders.aggregate[[
{
$count: "total"
}
]]
=> Kết quả:
/* 1 */
{
"total" : 5
}
5. $limit
và $skip
$limit
được dùng để giới hạn số lượng output document. $skip
được dùng để chỉ định số lượng document sẽ bị bỏ qua trong output [tính từ document đầu tiên]. $limit
và $skip
tương tự như LIMIT
và OFFSET
trong SQL.
Cú pháp:
{ $limit: }
{ $skip: }
Ví dụ:
Lấy order thứ 3 và thứ 4:
db.orders.aggregate[[
{
$skip: 2
},
{
$limit: 2
}
]]
=> Kết quả:
/* 1 */
{
"_id" : ObjectId["5dfed8b043e6fed50628908f"],
"cust_id" : "B456",
"products" : [
"apple",
"orange"
],
"amount" : 300,
"status" : "completed"
}
/* 2 */
{
"_id" : ObjectId["5dfed8b943e6fed506289094"],
"cust_id" : "C789",
"products" : [
"apple",
"lemon",
"orange"
],
"amount" : 800,
"status" : "completed"
}
6. $sort
$sort
được dùng để sắp xếp các document trong output theo một tiêu chí nào đó. $sort
tương tự như ORDER BY
trong SQL.
Cú pháp:
{ $sort: { : , : ... } }
Trong đó, có thể có các giá trị sau:
-
1
: sắp xếp theo thứ tự tăng dần -
-1
: sắp xếp theo thứ tự giảm dần
Ví dụ:
Sắp xếp các order theo thứ tự giảm dần của amount:
db.orders.aggregate[[
{
$sort: { amount: -1 }
}
]]
=> Kết quả:
/* 1 */
{
"_id" : ObjectId["5dfed8b943e6fed506289094"],
"cust_id" : "C789",
"products" : [
"apple",
"lemon",
"orange"
],
"amount" : 800,
"status" : "completed"
}
/* 2 */
{
"_id" : ObjectId["5dfed89743e6fed50628907c"],
"cust_id" : "A123",
"products" : [
"apple",
"lemon"
],
"amount" : 500,
"status" : "completed"
}
/* 3 */
{
"_id" : ObjectId["5dfed8b043e6fed50628908f"],
"cust_id" : "B456",
"products" : [
"apple",
"orange"
],
"amount" : 300,
"status" : "completed"
}
/* 4 */
{
"_id" : ObjectId["5dfed8c343e6fed506289097"],
"cust_id" : "A123",
"products" : [
"apple"
],
"amount" : 250,
"status" : "completed"
}
/* 5 */
{
"_id" : ObjectId["5dfed8a643e6fed50628908a"],
"cust_id" : "B456",
"products" : [
"lemon"
],
"amount" : 100,
"status" : "processing"
}
7. $group
$group
được dùng để gom nhóm các
input document theo expression _id
. Mỗi nhóm tương ứng với một output document. Trong $group
, chúng ta có thể sử dụng các accumulator expression như $sum
, $avg
, $max
, $min
, ...
$group
tương tự như GROUP BY
trong SQL.
Cú pháp:
{
$group:
{
_id: , // Group By Expression
: { : },
...
}
}
Nếu _id
được set bằng null
, MongoDB sẽ query tất cả các input document.
Ví dụ:
Gom nhóm các order theo cust_id
, đồng thời tính tổng amount của từng cust_id
:
db.orders.aggregate[[
{
$group: {
_id: "$cust_id",
total: { $sum: "$amount" }
}
}
]]
=> Kết quả:
/* 1 */
{
"_id" : "B456",
"total" : 400
}
/* 2 */
{
"_id" : "C789",
"total" : 800
}
/* 3 */
{
"_id" : "A123",
"total" : 750
}
8. $unwind
$unwind
được dùng để phân tách giá trị của một array field trong các input document. Nếu như array field của một input document có N phần tử thì trong output sẽ có N document.
Cú pháp:
{ $unwind: }
Ví dụ:
Mình sẽ thử áp dụng $unwind
với array field products
để xem kết quả nó sẽ như thế nào:
db.orders.aggregate[[
{
$unwind: "$products"
}
]]
=> Kết quả:
/* 1 */
{
"_id" : ObjectId["5dfed89743e6fed50628907c"],
"cust_id" : "A123",
"products" : "apple",
"amount" : 500,
"status" : "completed"
}
/* 2 */
{
"_id" : ObjectId["5dfed89743e6fed50628907c"],
"cust_id" : "A123",
"products" : "lemon",
"amount" : 500,
"status" : "completed"
}
/* 3 */
{
"_id" : ObjectId["5dfed8a643e6fed50628908a"],
"cust_id" : "B456",
"products" : "lemon",
"amount" : 100,
"status" : "processing"
}
/* 4 */
{
"_id" : ObjectId["5dfed8b043e6fed50628908f"],
"cust_id" : "B456",
"products" : "apple",
"amount" : 300,
"status" : "completed"
}
/* 5 */
{
"_id" : ObjectId["5dfed8b043e6fed50628908f"],
"cust_id" : "B456",
"products" : "orange",
"amount" : 300,
"status" : "completed"
}
/* 6 */
{
"_id" : ObjectId["5dfed8b943e6fed506289094"],
"cust_id" : "C789",
"products" : "apple",
"amount" : 800,
"status" : "completed"
}
/* 7 */
{
"_id" : ObjectId["5dfed8b943e6fed506289094"],
"cust_id" : "C789",
"products" : "lemon",
"amount" : 800,
"status" : "completed"
}
/* 8 */
{
"_id" : ObjectId["5dfed8b943e6fed506289094"],
"cust_id" : "C789",
"products" : "orange",
"amount" : 800,
"status" : "completed"
}
/* 9 */
{
"_id" : ObjectId["5dfed8c343e6fed506289097"],
"cust_id" : "A123",
"products" : "apple",
"amount" : 250,
"status" : "completed"
}
9. $lookup
$lookup
cho phép chúng ta thực hiện một phép left outer join giữa hai collection trong cùng một database.
Với mỗi input document, $lookup
sẽ thêm một array field chứa các phần tử matching với collection được join.
Cú pháp:
{
$lookup:
{
from: ,
localField: ,
foreignField: ,
as:
}
}
Trong đó:
-
from
collection không thể bị shard. -
as
có thể có tên bất kỳ, nhưng nếu một field nào đó trong document đã có tên như vậy thì giá trị của field đó sẽ bị ghi đè.
$lookup
tương đương với đoạn SQL sau:
SELECT *,
FROM collection
WHERE IN [SELECT *
FROM
WHERE = ];
Ví dụ:
Mình sẽ join orders
và customers
để bổ sung thêm thông tin của customer trong từng
order:
db.orders.aggregate[[
{
$lookup: {
from: "customers",
localField: "cust_id",
foreignField: "_id",
as: "customer"
}
}
]]
=> Kết quả:
/* 1 */
{
"_id" : ObjectId["5dfed89743e6fed50628907c"],
"cust_id" : "A123",
"products" : [
"apple",
"lemon"
],
"amount" : 500,
"status" : "completed",
"customer" : [
{
"_id" : "A123",
"name" : "Alice"
}
]
}
/* 2 */
{
"_id" : ObjectId["5dfed8a643e6fed50628908a"],
"cust_id" : "B456",
"products" : [
"lemon"
],
"amount" : 100,
"status" : "processing",
"customer" : []
}
/* 3 */
{
"_id" : ObjectId["5dfed8b043e6fed50628908f"],
"cust_id" : "B456",
"products" : [
"apple",
"orange"
],
"amount" : 300,
"status" : "completed",
"customer" : []
}
/* 4 */
{
"_id" : ObjectId["5dfed8b943e6fed506289094"],
"cust_id" : "C789",
"products" : [
"apple",
"lemon",
"orange"
],
"amount" : 800,
"status" : "completed",
"customer" : [
{
"_id" : "C789",
"name" : "Carol"
}
]
}
/* 5 */
{
"_id" : ObjectId["5dfed8c343e6fed506289097"],
"cust_id" : "A123",
"products" : [
"apple"
],
"amount" : 250,
"status" : "completed",
"customer" : [
{
"_id" : "A123",
"name" : "Alice"
}
]
}
10. Kết hợp các aggregation operator
Trong phần này, chúng ta sẽ kết hợp các aggregation operator trong một pipeline. Mình sẽ minh họa thông qua 2 ví dụ.
Ví dụ 1
Yêu cầu:
- Lọc tất cả các order có
status
làcompleted
. - Gom nhóm các order có được ở bước 1 theo
cust_id
và tính tổngamount
. - Sắp xếp theo tổng amount giảm dần.
db.orders.aggregate[[
{
$match: {
status: "completed"
}
},
{
$group: {
_id: "$cust_id",
total: { $sum: "$amount" }
}
},
{
$sort: { "total": -1 }
}
]]
=> Kết quả:
/* 1 */
{
"_id" : "C789",
"total" : 800
}
/* 2 */
{
"_id" : "A123",
"total" : 750
}
/* 3 */
{
"_id" : "B456",
"total" : 300
}
Ví dụ 2
Tiếp tục ví dụ 1, chúng ta sẽ bổ sung thêm customer name cho các output document. Các bạn lưu ý chúng ta sẽ chỉ bổ sung customer name mà thôi, còn customer ID thì chúng ta đã lưu trong _id
rồi.
db.orders.aggregate[[
{
$match: {
status: "completed"
}
},
{
$group: {
_id: "$cust_id",
total: { $sum: "$amount" }
}
},
{
$sort: { "total": -1 }
},
{
$lookup: {
from: "customers",
localField: "_id",
foreignField: "_id",
as: "customer"
}
},
{
$project: {
total: 1,
cust_name: "$customer.name"
}
},
{
$unwind: "$cust_name"
}
]]
=> Kết quả:
/* 1 */
{
"_id" : "C789",
"total" : 800,
"cust_name" : "Carol"
}
/* 2 */
{
"_id" : "A123",
"total" : 750,
"cust_name" : "Alice"
}
/* 3 */
{
"_id" : "B456",
"total" : 300,
"cust_name" : "Bob"
}
30 bài viết.
282 người follow
{{userFollowed ? 'Following' : 'Follow'}}
Cùng một tác giả
62 42
MyContact là một ứng dụng mà mình thường viết mỗi khi học một ngôn ngữ hay công nghệ mới. MyContact chỉ là một ứng dụng CRUD đơn giản, cho phép ngư...
62 42
49 17
Hướng dẫn lập trình Spring Security Trong bài viết lần này, mình sẽ giúp các bạn bước đầu tìm hiểu [Link] thông qua xây dựng các chức năng: Đăng ...
49 17
23 0
Trước đây khi mới học Spring, mình thường nhảy thẳng lên tìm hiểu các project như Spring MVC hay Spring Boot để viết ứng dụng, thỉnh thoảng mới ngó...
23 0
Bài viết liên quan
45 7
Giới thiệu MongoDB là một giải pháp nosql database. Data được lưu ở dạng các bson document. Hỗ trợ vertical scaling và horizontal scaling, dynamic...
45 7
31 13
Quá trình lột xác ngoạn mục của một hệ thống cổ lỗ sĩ khi được thiết kế cẩn thận: 1 usecase thành công của việc áp dụng triệt để các phương pháp xử...
31 13
0 0
//grokonez.com/nodejs/nodejsexpressrestapiuploadimportcsvfiledatatomongodbusingcsvtojsonmulter Nodejs Express RestAPI – Upload/Import CSV fi...
0 0