在mongodb中删除重复文档的最快方法

ewo*_*com 34 optimization performance duplicates mongodb

我在mongodb中有大约170万份文件(将来10m +).其中一些代表我不想要的重复条目.文档的结构是这样的:

{
    _id: 14124412,
    nodes: [
        12345,
        54321
        ],
    name: "Some beauty"
}
Run Code Online (Sandbox Code Playgroud)

如果文档至少有一个节点与具有相同名称的另一个文档相同,则文档是重复的.删除重复项的最快方法是什么?

Som*_*luk 65

dropDups: true 选项在3.0中不可用.

我有聚合框架的解决方案,用于收集重复项,然后一次性删除.

它可能比系统级"索引"更改慢一些.但考虑到你想删除重复文件的方式很好.

一个.一次性删除所有文档

var duplicates = [];

db.collectionName.aggregate([
  { $match: { 
    name: { "$ne": '' }  // discard selection criteria
  }},
  { $group: { 
    _id: { name: "$name"}, // can be grouped on multiple properties 
    dups: { "$addToSet": "$_id" }, 
    count: { "$sum": 1 } 
  }}, 
  { $match: { 
    count: { "$gt": 1 }    // Duplicates considered as count greater than one
  }}
],
{allowDiskUse: true}       // For faster processing if set is larger
)               // You can display result until this and check duplicates 
.forEach(function(doc) {
    doc.dups.shift();      // First element skipped for deleting
    doc.dups.forEach( function(dupId){ 
        duplicates.push(dupId);   // Getting all duplicate ids
        }
    )    
})

// If you want to Check all "_id" which you are deleting else print statement not needed
printjson(duplicates);     

// Remove all duplicates in one go    
db.collectionName.remove({_id:{$in:duplicates}})  
Run Code Online (Sandbox Code Playgroud)

湾 您可以逐个删除文档.

db.collectionName.aggregate([
  // discard selection criteria, You can remove "$match" section if you want
  { $match: { 
    source_references.key: { "$ne": '' }  
  }},
  { $group: { 
    _id: { source_references.key: "$source_references.key"}, // can be grouped on multiple properties 
    dups: { "$addToSet": "$_id" }, 
    count: { "$sum": 1 } 
  }}, 
  { $match: { 
    count: { "$gt": 1 }    // Duplicates considered as count greater than one
  }}
],
{allowDiskUse: true}       // For faster processing if set is larger
)               // You can display result until this and check duplicates 
.forEach(function(doc) {
    doc.dups.shift();      // First element skipped for deleting
    db.collectionName.remove({_id : {$in: doc.dups }});  // Delete remaining duplicates
})
Run Code Online (Sandbox Code Playgroud)

  • 感谢您提供有用的答案!我确实发现它更好,当你有很多行(我有 5M 行)时,最好创建一个计数器并为每 10K 限制它,而不是为整个重复项限制它,因为它可能太大了:) (2认同)
  • 注意:根据 https://docs.mongodb.com/manual/reference/operator/update/addToSet/,_$addToSet 不保证修改后的 set_ 中元素的特定顺序......如果你使用`$push`我想保留顺序...另外注意:`$addToSet` 不会向数组添加重复项,而 `$push` 会 (2认同)

Joh*_*yHK 43

假设您要永久删除包含集合中的重复name+ nodes条目的文档,您可以unique使用以下dropDups: true选项添加索引:

db.test.ensureIndex({name: 1, nodes: 1}, {unique: true, dropDups: true}) 
Run Code Online (Sandbox Code Playgroud)

正如文档所说,使用它会非常谨慎,因为它会从数据库中删除数据.首先备份数据库,以防它没有按照您的预期完成.

UPDATE

此解决方案仅在MongoDB 2.x中有效,因为该dropDups选项在3.0(docs)中不再可用.

  • @ user1188570它是复合的,因此两个字段在同一文档中必须具有副本 (4认同)
  • 您可能希望在删除重复项后删除索引:`db.test.dropIndex({name: 1,nodes: 1})` (2认同)

dhy*_*sba 22

使用mongodump创建集合转储

清晰的收藏

添加唯一索引

使用mongorestore恢复集合


小智 14

以下 Mongo 聚合管道执行重复数据删除并将其输出回相同或不同的集合。

collection.aggregate([
  { $group: {
    _id: '$field_to_dedup',
    doc: { $first: '$$ROOT' }
  } },
  { $replaceRoot: {
    newRoot: '$doc'
  } },
  { $out: 'collection' }
], { allowDiskUse: true })
Run Code Online (Sandbox Code Playgroud)


Ali*_*awa 7

我发现这个解决方案适用于MongoDB 3.4:我假设带有重复项的字段称为fieldX

db.collection.aggregate([
{
    // only match documents that have this field
    // you can omit this stage if you don't have missing fieldX
    $match: {"fieldX": {$nin:[null]}}  
},
{
    $group: { "_id": "$fieldX", "doc" : {"$first": "$$ROOT"}}
},
{
    $replaceRoot: { "newRoot": "$doc"}
}
],
{allowDiskUse:true})
Run Code Online (Sandbox Code Playgroud)

作为mongoDB的新手,我花了很多时间并使用其他冗长的解决方案来查找和删除重复项.但是,我认为这个解决方案简洁易懂.

它的工作原理是首先匹配包含fieldX的文档(我有一些没有这个字段的文档,我有一个额外的空结果).

下一阶段按字段X对文档进行分组,并且仅使用$$ ROOT在每个组中插入$ first文档.最后,它使用$ first和$$ ROOT找到的文档替换整个聚合组.

我不得不添加allowDiskUse,因为我的集合很大.

您可以在任意数量的管道之后添加它,虽然$ first的文档在使用$ first之前提到了一个排序阶段,但它没有它对我有用."不能在这里发布链接,我的声誉不到10 :("

您可以通过添加$ out阶段将结果保存到新集合中......

或者,如果一个人只对几个字段感兴趣,例如field1,field2,而不是整个文档,则在没有replaceRoot的组阶段:

db.collection.aggregate([
{
    // only match documents that have this field
    $match: {"fieldX": {$nin:[null]}}  
},
{
    $group: { "_id": "$fieldX", "field1": {"$first": "$$ROOT.field1"}, "field2": { "$first": "$field2" }}
}
],
{allowDiskUse:true})
Run Code Online (Sandbox Code Playgroud)