Để sắp xếp tài liệu dựa trên giá trị trong mảng các tài liệu con (subdocuments), MongoDB sử dụng Aggregation Pipeline với các stage: $match, $unwind, $project, $sort, $limit.
Tạo dữ liệu mẫu
> db.sortBySubDocumentsDemo.insertOne(
{
"StudentName": "Chris",
"StudentDetails": [
{ "Age":21, "StudentScore":91 },
{ "Age":22, "StudentScore":99 },
{ "Age":21, "StudentScore":93 }
]
}
);
{ "acknowledged" : true, "insertedId" : ObjectId("5cd57e297924bb85b3f48942") }
> db.sortBySubDocumentsDemo.insertOne(
{
"StudentName": "Robert",
"StudentDetails": [
{ "Age":24, "StudentScore":78 },
{ "Age":21, "StudentScore":86 },
{ "Age":23, "StudentScore":45 }
]
}
);
{ "acknowledged" : true, "insertedId" : ObjectId("5cd57e4c7924bb85b3f48943") }
Xem dữ liệu
> db.sortBySubDocumentsDemo.find().pretty();
Sắp xếp theo StudentScore của subdocument có Age = 21
> db.sortBySubDocumentsDemo.aggregate([ {$match: {'StudentDetails.Age': 21 }}, {$unwind: '$StudentDetails'}, {$match: {'StudentDetails.Age': 21}}, {$project: {_id: 0, "StudentName": 1, 'StudentDetails.StudentScore': 1}}, {$sort: { 'StudentDetails.StudentScore': 1 }}, {$limit: 5} ]);Kết quả
Giải thích từng stage
- $match (1): Lọc trước tài liệu cha có ít nhất một subdocument Age=21 (giảm dữ liệu xử lý)
- $unwind: "Phẳng hóa" mảng StudentDetails → mỗi subdocument thành một tài liệu riêng
- $match (2): Lọc tiếp chỉ giữ subdocument có Age=21
- $project: Chọn trường cần thiết, loại _id, giữ StudentName và StudentScore
- $sort: Sắp xếp tăng dần theo StudentScore (1 = asc, -1 = desc)
- $limit: Giới hạn 5 kết quả
Tại sao cần 2 $match?
- Match đầu: Lọc ở mức tài liệu cha (hiệu quả hơn, giảm dữ liệu vào $unwind)
- Match sau: Lọc ở mức subdocument sau khi đã unwind
Mở rộng: Giữ cấu trúc mảng gốc
Nếu muốn giữ mảng và chỉ sắp xếp tài liệu cha theo max/min score của subdocument Age=21:
db.sortBySubDocumentsDemo.aggregate([
{$match: {'StudentDetails.Age': 21}},
{$addFields: {
minScoreAge21: {$min: "$StudentDetails.StudentScore"},
maxScoreAge21: {$max: "$StudentDetails.StudentScore"}
}},
{$sort: {minScoreAge21: 1}}
]);