Giới thiệu
Để thêm trường (property) mới cho tất cả document trong một collection lớn, bạn dùng updateMany() (hiện đại) hoặc forEach() với updateOne() (cũ). Với collection rất lớn, cần xử lý theo batch để tránh timeout và giảm tải server.
Ví dụ minh họa
1. Tạo collection
> db.themThuocTinh.insertOne({ "TenSinhVien": "John", "TuoiSinhVien": 23, "QuocGia": "US" });
{ "acknowledged" : true, "insertedId" : ObjectId("5ca1e61866324ffac2a7dc56") }
> db.themThuocTinh.insertOne({ "TenSinhVien": "David", "TuoiSinhVien": 21, "QuocGia": "AUS" });
{ "acknowledged" : true, "insertedId" : ObjectId("5ca1e62366324ffac2a7dc57") }
> db.themThuocTinh.insertOne({ "TenSinhVien": "Bob", "TuoiSinhVien": 21, "QuocGia": "UK" });
{ "acknowledged" : true, "insertedId" : ObjectId("5ca1e62d66324ffac2a7dc58") }
2. Thêm trường TenSinhVienInUpperCase = uppercase(TenSinhVien)
> db.themThuocTinh.find().forEach(function(doc) {
db.themThuocTinh.updateOne(
{ _id: doc._id },
{ $set: { TenSinhVienInUpperCase: doc.TenSinhVien.toUpperCase() } }
);
});
3. Kết quả
{ "_id" : ObjectId("5ca1e61866324ffac2a7dc56"), "TenSinhVien" : "John", "TuoiSinhVien" : 23, "QuocGia" : "US", "TenSinhVienInUpperCase" : "JOHN" }
{ "_id" : ObjectId("5ca1e62366324ffac2a7dc57"), "TenSinhVien" : "David", "TuoiSinhVien" : 21, "QuocGia" : "AUS", "TenSinhVienInUpperCase" : "DAVID" }
{ "_id" : ObjectId("5ca1e62d66324ffac2a7dc58"), "TenSinhVien" : "Bob", "TuoiSinhVien" : 21, "QuocGia" : "UK", "TenSinhVienInUpperCase" : "BOB" }
Phương pháp hiện đại: updateMany với Aggregation Pipeline (MongoDB 4.2+)
Hiệu quả hơn, atomic, không cần vòng lặp:
db.themThuocTinh.updateMany(
{}, // Tất cả document
[
{ $set: {
TenSinhVienInUpperCase: { $toUpper: "$TenSinhVien" }
} }
]
);
Xử lý Collection LỚN (Batch Processing)
Với collection hàng triệu document, dùng batch để tránh timeout và OOM:
Cách 1: Cursor batchSize + forEach (Shell)
var batchSize = 1000;
var count = 0;
db.themThuocTinh.find({ TenSinhVienInUpperCase: { $exists: false } })
.batchSize(batchSize)
.forEach(function(doc) {
db.themThuocTinh.updateOne(
{ _id: doc._id },
{ $set: { TenSinhVienInUpperCase: doc.TenSinhVien.toUpperCase() } }
);
count++;
if (count % 10000 === 0) {
print("Đã xử lý: " + count);
sleep(100); // Nghỉ 100ms để giảm tải
}
});
print("Hoàn tất: " + count + " documents");
Cách 2: Bulk Operations (Hiệu quả nhất)
var bulk = db.themThuocTinh.initializeUnorderedBulkOp();
var count = 0;
db.themThuocTinh.find({ TenSinhVienInUpperCase: { $exists: false } }).forEach(function(doc) {
bulk.find({ _id: doc._id }).updateOne({
$set: { TenSinhVienInUpperCase: doc.TenSinhVien.toUpperCase() }
});
count++;
if (count % 1000 === 0) {
bulk.execute();
bulk = db.themThuocTinh.initializeUnorderedBulkOp();
}
});
if (count % 1000 !== 0) {
bulk.execute();
}
Cách 3: Application code (Node.js/Python) - Khuyên dùng cho production
// Node.js example
const cursor = collection.find({ newField: { $exists: false } }).batchSize(1000);
const bulkOps = [];
for await (const doc of cursor) {
bulkOps.push({
updateOne: {
filter: { _id: doc._id },
update: { $set: { newField: doc.existingField.toUpperCase() } }
}
});
if (bulkOps.length >= 1000) {
await collection.bulkWrite(bulkOps);
bulkOps.length = 0;
}
}
if (bulkOps.length > 0) await collection.bulkWrite(bulkOps);
So sánh phương pháp
| Phương pháp | Ưu điểm | Nhược điểm | Phù hợp |
|---|---|---|---|
| updateMany + pipeline | Atomic, nhanh, code gọn | Chỉ MongoDB 4.2+, logic phức tạp hạn chế | Collection < 100k doc |
| forEach + updateOne | Đơn giản, logic JS tùy ý | Chậm, single-threaded, risk timeout | Collection nhỏ, ad-hoc |
| Bulk Write | Nhanh, kiểm soát batch, ít round-trip | Phức tạp hơn | Collection lớn, production |
Lưu ý quan trọng
- Thêm filter
{ newField: { $exists: false } }để idempotent (chạy lại an toàn) - Monitor
db.currentOp()vàdb.serverStatus().opcounterskhi chạy - Cân nhắc chạy ở off-peak hours
- Backup trước khi update hàng loạt