หาเอกสารซ้ำ (Duplicate) ตามฟิลด์เฉพาะด้วย Aggregation
MongoDB 3.x ไม่มีคำสั่งลบซ้ำโดยตรง ใช้ Aggregation Pipeline หา _id ของเอกสารซ้ำ แล้วค่อยลบด้วยโค้ดเพิ่มเติม
ตัวอย่างข้อมูลทดสอบ (มี FirstName ซ้ำ)
> db.demo438.insertOne({ "FirstName": "Chris" });
> db.demo438.insertOne({ "FirstName": "David" });
> db.demo438.insertOne({ "FirstName": "Chris" });
> db.demo438.insertOne({ "FirstName": "Bob" });
> db.demo438.insertOne({ "FirstName": "David" });
แสดงข้อมูลทั้งหมด
> db.demo438.find();
ผลลัพธ์: 5 เอกสาร (Chris 2, David 2, Bob 1)
คำสั่ง Aggregation หา Duplicate Group
> db.demo438.aggregate([
{ $group: { _id: { FirstName: "$FirstName" }, DuplicateValueIds: { $addToSet: "$_id" } } }
]);
ผลลัพธ์ที่ได้ (กลุ่มตาม FirstName พร้อม _id ทั้งหมดในกลุ่ม)
{ "_id": { "FirstName": "David" }, "DuplicateValueIds": [ ObjectId("..."), ObjectId("...") ] }
{ "_id": { "FirstName": "Bob" }, "DuplicateValueIds": [ ObjectId("...") ] }
{ "_id": { "FirstName": "Chris" }, "DuplicateValueIds": [ ObjectId("..."), ObjectId("...") ] }
คำอธิบาย
- $group: { _id: { FirstName: "$FirstName" }, ... }: จัดกลุ่มตามค่า FirstName
- $addToSet: "$_id": สะสม _id ของทุกเอกสารในกลุ่มเป็นอาร์เรย์ (Unique)
- กลุ่มที่มี DuplicateValueIds.length > 1 = มีข้อมูลซ้ำ
วิธีลบข้อมูลซ้ำ (เก็บตัวแรก ลบตัวหลัง)
รัน Aggregation แล้ววนลูปลบใน Application Code:
var duplicates = db.demo438.aggregate([
{ $group: { _id: { FirstName: "$FirstName" }, ids: { $addToSet: "$_id" }, count: { $sum: 1 } } },
{ $match: { count: { $gt: 1 } } }
]);
duplicates.forEach(function(group) {
// เก็บตัวแรก (ids[0]) ลบตัวที่เหลือ (ids.slice(1))
group.ids.slice(1).forEach(function(id) {
db.demo438.deleteOne({ _id: id });
});
});
วิธีทันสมัยกว่า (MongoDB 4.2+): ใช้ $merge หรือ $out
> db.demo438.aggregate([
{ $sort: { _id: 1 } },
{ $group: { _id: "$FirstName", doc: { $first: "$$ROOT" } } },
{ $replaceRoot: { newRoot: "$doc" } },
{ $merge: { into: "demo438_dedup", on: "_id", whenMatched: "replace", whenNotMatched: "insert" } }
]);
สร้างคอลเลกชันใหม่ที่ไม่มีข้อมูลซ้ำ
สร้าง Unique Index ป้องกันข้อมูลซ้ำในอนาคต
> db.demo438.createIndex({ FirstName: 1 }, { unique: true });
จะป้องกัน Insert ซ้ำได้ (Throw Duplicate Key Error)