Để phát hiện và xóa các tài liệu trùng lặp (dựa trên giá trị trường, không phải _id) trong MongoDB 3.x (trước khi có deleteMany với filter phức tạp), cách phổ biến là dùng Aggregation $group để tìm nhóm trùng lặp, lấy danh sách _id, rồi xóa thủ công các _id thừa.
1. Tạo dữ liệu có trùng FirstName
> db.demo438.insertOne({ "FirstName": "Chris" });
> db.demo438.insertOne({ "FirstName": "David" });
> db.demo438.insertOne({ "FirstName": "Chris" }); // Trùng
> db.demo438.insertOne({ "FirstName": "Bob" });
> db.demo438.insertOne({ "FirstName": "David" }); // Trùng
2. Aggregation: Nhóm theo FirstName, gom _id
> db.demo438.aggregate([
{ $group: { _id: { FirstName: "$FirstName" }, DuplicateValueIds: { $addToSet: "$_id" } } }
]);
3. Kết quả: Các nhóm trùng có mảng _id > 1 phần tử
{ "_id": { "FirstName": "David" }, "DuplicateValueIds": [ ObjectId("5e775c47..."), ObjectId("5e775c3d...") ] }
{ "_id": { "FirstName": "Bob" }, "DuplicateValueIds": [ ObjectId("5e775c44...") ] }
{ "_id": { "FirstName": "Chris" }, "DuplicateValueIds": [ ObjectId("5e775c40..."), ObjectId("5e775c37...") ] }
4. Xóa thủ công (logic: giữ _id đầu tiên, xóa các _id còn lại)
// Ví dụ xóa David trùng (giữ ObjectId("5e775c3d..."))
db.demo438.deleteOne({ _id: ObjectId("5e775c47...") });
// Ví dụ xóa Chris trùng (giữ ObjectId("5e775c37..."))
db.demo438.deleteOne({ _id: ObjectId("5e775c40...") });
Cách tự động hóa (Script trong Shell):
db.demo438.aggregate([
{ $group: { _id: { FirstName: "$FirstName" }, ids: { $push: "$_id" }, count: { $sum: 1 } } },
{ $match: { count: { $gt: 1 } } }
]).forEach(doc => {
doc.ids.slice(1).forEach(id => db.demo438.deleteOne({ _id: id })); // Giữ ids[0], xóa phần còn lại
});
MongoDB 4.2+ (Dễ hơn): Dùng deleteMany với $expr hoặc tạo unique index với dropDups: true (cẩn thận: dropDups xóa data ngẫu nhiên, đã deprecated).
Best Practice: Tạo **Unique Index** ngay từ đầu: db.demo438.createIndex({ FirstName: 1 }, { unique: true }) để ngăn chặn trùng lặp tại nguồn.