Giới thiệu
Để lấy ngẫu nhiên (random) N tài liệu từ collection, MongoDB cung cấp stage aggregation $sample (từ version 3.2). Đây là cách hiệu quả và đơn giản nhất.
Cú pháp
db.collection.aggregate([ { $sample: { size: N } } ]);
Ví dụ minh họa
1. Tạo collection 6 document
> db.ngauNhien2Doc.insertOne({ "MaSinhVien": 10 });
{ "acknowledged" : true, "insertedId" : ObjectId("5c9ec9aad628fa4220163b87") }
> db.ngauNhien2Doc.insertOne({ "MaSinhVien": 100 });
{ "acknowledged" : true, "insertedId" : ObjectId("5c9ec9add628fa4220163b88") }
> db.ngauNhien2Doc.insertOne({ "MaSinhVien": 45 });
{ "acknowledged" : true, "insertedId" : ObjectId("5c9ec9b0d628fa4220163b89") }
> db.ngauNhien2Doc.insertOne({ "MaSinhVien": 55 });
{ "acknowledged" : true, "insertedId" : ObjectId("5c9ec9b3d628fa4220163b8a") }
> db.ngauNhien2Doc.insertOne({ "MaSinhVien": 5 });
{ "acknowledged" : true, "insertedId" : ObjectId("5c9ec9b7d628fa4220163b8b") }
> db.ngauNhien2Doc.insertOne({ "MaSinhVien": 7 });
{ "acknowledged" : true, "insertedId" : ObjectId("5c9ec9bad628fa4220163b8c") }
2. Lấy 2 document ngẫu nhiên (Lần 1)
> db.ngauNhien2Doc.aggregate([ { $sample: { size: 2 } } ]);
{ "_id" : ObjectId("5c9ec9b3d628fa4220163b8a"), "MaSinhVien" : 55 }
{ "_id" : ObjectId("5c9ec9aad628fa4220163b87"), "MaSinhVien" : 10 }
3. Chạy lại lần 2 - Kết quả khác (ngẫu nhiên thực sự)
> db.ngauNhien2Doc.aggregate([ { $sample: { size: 2 } } ]);
{ "_id" : ObjectId("5c9ec9add628fa4220163b88"), "MaSinhVien" : 100 }
{ "_id" : ObjectId("5c9ec9b7d628fa4220163b8b"), "MaSinhVien" : 5 }
Chi tiết về $sample
| Đặc điểm | Mô tả |
|---|---|
| Algorithm | Reservoir sampling (lấy mẫu hồ chứa) |
| Hiệu suất | O(N) - quét toàn bộ collection |
| Độ chính xác | Xấp xỉ (approximate) cho collection lớn |
| Size > collection size | Trả về tất cả document (không lặp) |
| Sharded cluster | Hỗ trợ từ MongoDB 4.4+ |
Lưu ý quan trọng
$samplephải là **stage đầu tiên** trong pipeline (trước $match, $project...)- Không thể dùng index - luôn collection scan
- Với collection lớn (>100k docs), cân nhắc cách khác để tránh chậm
- Kết quả **không đảm bảo phân bố đều** hoàn hảo trên collection cực lớn
Phương án thay thế cho collection lớn
Cách 1: Random skip (nếu có index trên _id)
var count = db.collection.countDocuments();
var randomSkip = Math.floor(Math.random() * count);
db.collection.find().skip(randomSkip).limit(2);
Cách 2: Pre-computed random field
// Thêm trường random khi insert
db.collection.insertOne({ ..., random: Math.random() });
// Query
db.collection.find({ random: { $gte: Math.random() } }).limit(2);
Cách 3: Lấy _id ngẫu nhiên từ application
// App code: lấy count, random N offset, query bằng _id
const count = await coll.countDocuments();
const randomIds = Array.from({length: 2}, () => Math.floor(Math.random() * count));
const docs = await coll.find({ _id: { $in: randomIds } }).toArray();