การสุ่มเอกสาร (Random Sampling) ใน MongoDB ด้วย $sample
ใช้ Aggregation Stage $sample เพื่อสุ่มเอกสารจาก Collection ได้อย่างมีประสิทธิภาพ โดยระบุ size คือจำนวนเอกสารที่ต้องการ
ตัวอย่างการสร้างคอลเลกชันและเพิ่มข้อมูล 6 รายการ
> db.twoRandomDocumentDemo.insertOne({"StudentId":10});
{ "acknowledged" : true, "insertedId" : ObjectId("5c9ec9aad628fa4220163b87") }
> db.twoRandomDocumentDemo.insertOne({"StudentId":100});
{ "acknowledged" : true, "insertedId" : ObjectId("5c9ec9add628fa4220163b88") }
> db.twoRandomDocumentDemo.insertOne({"StudentId":45});
{ "acknowledged" : true, "insertedId" : ObjectId("5c9ec9b0d628fa4220163b89") }
> db.twoRandomDocumentDemo.insertOne({"StudentId":55});
{ "acknowledged" : true, "insertedId" : ObjectId("5c9ec9b3d628fa4220163b8a") }
> db.twoRandomDocumentDemo.insertOne({"StudentId":5});
{ "acknowledged" : true, "insertedId" : ObjectId("5c9ec9b7d628fa4220163b8b") }
> db.twoRandomDocumentDemo.insertOne({"StudentId":7});
{ "acknowledged" : true, "insertedId" : ObjectId("5c9ec9bad628fa4220163b8c") }
แสดงข้อมูลทั้งหมด
> db.twoRandomDocumentDemo.find();
ผลลัพธ์ (6 เอกสาร):
{ "_id" : ObjectId("5c9ec9aad628fa4220163b87"), "StudentId" : 10 }
{ "_id" : ObjectId("5c9ec9add628fa4220163b88"), "StudentId" : 100 }
{ "_id" : ObjectId("5c9ec9b0d628fa4220163b89"), "StudentId" : 45 }
{ "_id" : ObjectId("5c9ec9b3d628fa4220163b8a"), "StudentId" : 55 }
{ "_id" : ObjectId("5c9ec9b7d628fa4220163b8b"), "StudentId" : 5 }
{ "_id" : ObjectId("5c9ec9bad628fa4220163b8c"), "StudentId" : 7 }
สุ่ม 2 เอกสารจาก 6 (Run ครั้งที่ 1)
> db.twoRandomDocumentDemo.aggregate([{$sample: {size: 2}}]);
ผลลัพธ์ (ตัวอย่าง):
{ "_id" : ObjectId("5c9ec9b3d628fa4220163b8a"), "StudentId" : 55 }
{ "_id" : ObjectId("5c9ec9aad628fa4220163b87"), "StudentId" : 10 }
สุ่ม 2 เอกสารจาก 6 (Run ครั้งที่ 2 - ผลลัพธ์ต่างออกไป)
> db.twoRandomDocumentDemo.aggregate([{$sample: {size: 2}}]);
ผลลัพธ์ (ตัวอย่าง):
{ "_id" : ObjectId("5c9ec9add628fa4220163b88"), "StudentId" : 100 }
{ "_id" : ObjectId("5c9ec9b7d628fa4220163b8b"), "StudentId" : 5 }
คำอธิบาย $sample
- {$sample: {size: N}} : สุ่ม N เอกสารจาก Collection
- Algorithm: ใช้ Reservoir Sampling (เหมาะกับ Collection ใหญ่) หรือ Random Cursor (Collection เล็ก)
- Non-deterministic: ผลลัพธ์ต่างกันทุกครั้งที่รัน
- No Sort Order: ไม่รับประกันลำดับ
ข้อจำกัดและข้อควรระวัง
⚠️ ข้อจำกัดของ $sample
- Size ต้อง <= 5% ของ Collection Size (สำหรับ Reservoir Sampling) มิฉะนั้นจะ Fallback เป็น Full Collection Scan
- ไม่รองรับ Sharded Collection ต้องใช้ $sample บน mongos ได้แต่มีข้อจำกัด
- ไม่สามารถใช้ $match ก่อน $sample ได้ (ต้อง $match ก่อน แล้ว $sample) - รองรับใน Pipeline
- Replacement: Sampling แบบ Without Replacement (ไม่ซ้ำในรอบเดียวกัน)
ตัวอย่าง Pipeline ครบถ้วน: Filter แล้วค่อย Random
// สุ่ม 3 เอกสารจากเฉพาะ Active Users
db.users.aggregate([
{ $match: { status: "active", age: { $gte: 18 } } },
{ $sample: { size: 3 } },
{ $project: { _id: 1, name: 1, email: 1 } }
]);
วิธีสุ่มแบบเก่า (ก่อนมี $sample) - ไม่แนะนำ
// ใช้ $rand (MongoDB 4.4+) + sort + limit
db.collection.aggregate([
{ $addFields: { rand: { $rand: {} } } },
{ $sort: { rand: 1 } },
{ $limit: 2 }
]);
// ใช้ skip แบบสุ่ม (ไม่แนะนำ - ช้ามาก)
var count = db.collection.countDocuments();
var randomSkip = Math.floor(Math.random() * count);
db.collection.find().skip(randomSkip).limit(2);
ใช้ $sample กับ $match และ $project
db.products.aggregate([
{ $match: { category: "electronics", price: { $lt: 1000 } } },
{ $sample: { size: 5 } },
{ $project: { _id: 0, name: 1, price: 1, image: 1 } }
]);
การใช้งานใน Application (Node.js)
const samples = await db.collection('products')
.aggregate([
{ $match: { inStock: true } },
{ $sample: { size: 10 } }
])
.toArray();
$sample เป็นวิธีที่มีประสิทธิภาพและง่ายที่สุดสำหรับ Random Sampling ใน MongoDB เวอร์ชันปัจจุบัน