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

在mongodb中删除重复文档的最快方法,mongodb,performance,optimization,duplicates,Mongodb,Performance,Optimization,Duplicates,我在mongodb中拥有大约170万份文档(未来1000多万份)。其中一些代表我不想要的重复条目。文档的结构如下所示: { _id: 14124412, nodes: [ 12345, 54321 ], name: "Some beauty" } 如果文档至少有一个节点与另一个具有相同名称的文档相同,则该文档是重复的。删除重复项的最快方法是什么?如果要从集合中永久删除包含重复的名称+节点项的文档,可以使用以下选项添加唯一索

我在mongodb中拥有大约170万份文档(未来1000多万份)。其中一些代表我不想要的重复条目。文档的结构如下所示:

{
    _id: 14124412,
    nodes: [
        12345,
        54321
        ],
    name: "Some beauty"
}

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

如果要从集合中永久删除包含重复的
名称
+
节点
项的文档,可以使用以下选项添加
唯一
索引:

正如文档中所说的那样,在这方面要格外小心,因为它会从数据库中删除数据。首先备份数据库,以防它不能完全按照您的预期运行

更新


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

dropDups:true
选项在3.0中不可用

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

它可能比系统级“索引”更改慢一些。但考虑到您想要删除重复文档的方式,这是很好的

a。一次性删除所有文档

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}})  
b。您可以逐个删除文档

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
})

使用mongodump创建集合转储

清册

添加唯一索引


使用mongorestore恢复收藏

我找到了这个与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})
作为mongoDB的新手,我花了很多时间并使用其他冗长的解决方案来查找和删除重复项。然而,我认为这个解决方案简洁易懂

它首先匹配包含fieldX的文档(我有一些文档没有这个字段,我得到了一个额外的空结果)

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

我不得不添加allowDiskUse,因为我的收藏量很大

您可以在任意数量的管道之后添加这个,尽管$first的文档在使用$first之前提到了排序阶段,但在没有它的情况下,它对我是有效的。“无法在此发布链接,我的声誉不足10:(”

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

或者,如果一个人只对几个字段感兴趣,例如字段1、字段2,而不是整个文档,则在分组阶段不使用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})

以下是一种稍微“手动”的方法:

基本上,首先,获取一个您感兴趣的所有唯一密钥的列表

然后使用每个键执行搜索,如果搜索返回的值大于1,则删除

  db.collection.distinct("key").forEach((num)=>{
    var i = 0;
    db.collection.find({key: num}).forEach((doc)=>{
      if (i)   db.collection.remove({key: num}, { justOne: true })
      i++
    })
  });
  • 总体思路是使用findOne 从集合中的重复记录中检索一个随机id

  • 删除集合中除从findOne选项检索的随机id以外的所有记录

  • 如果你想在pymongo这样做,你可以这样做

    def _run_query():
    
            try:
    
                for record in (aggregate_based_on_field(collection)):
                    if not record:
                        continue
                    _logger.info("Working on Record %s", record)
    
                    try:
                        retain = db.collection.find_one(find_one({'fie1d1': 'x',  'field2':'y'}, {'_id': 1}))
                        _logger.info("_id to retain from duplicates %s", retain['_id'])
    
                        db.collection.remove({'fie1d1': 'x',  'field2':'y', '_id': {'$ne': retain['_id']}})
    
                    except Exception as ex:
                        _logger.error(" Error when retaining the record :%s Exception: %s", x, str(ex))
    
            except Exception as e:
                _logger.error("Mongo error when deleting duplicates %s", str(e))
    
    
    def aggregate_based_on_field(collection):
        return collection.aggregate([{'$group' : {'_id': "$fieldX"}}])
    
    从外壳:

  • 将find_one替换为findOne
  • 相同的remove命令应该可以工作

  • 下面的方法合并具有相同名称的文档,同时只保留唯一的节点,而不复制它们

    我发现使用
    $out
    操作符是一种简单的方法。我将数组展开,然后通过添加到集合对其进行分组。
    $out
    操作符允许聚合结果保持不变。 如果您输入集合本身的名称,它将用新数据替换集合。如果名称不存在,它将创建一个新集合

    希望这有帮助

    allowDiskUse
    可能必须添加到管道中

    db.collectionName.aggregate([
      {
        $unwind:{path:"$nodes"},
      },
      {
        $group:{
          _id:"$name",
          nodes:{
            $addToSet:"$nodes"
          }
      },
      {
        $project:{
          _id:0,
          name:"$_id.name",
          nodes:1
        }
      },
      {
        $out:"collectionNameWithoutDuplicates"
      }
    ])
    
    使用pymongo应该可以做到这一点

    在唯一字段中为集合添加需要唯一的字段

    unique_field = {"field1":"$field1","field2":"$field2"}
    
    cursor = DB.COL.aggregate([{"$group":{"_id":unique_field, "dups":{"$push":"$uuid"}, "count": {"$sum": 1}}},{"$match":{"count": {"$gt": 1}}},{"$group":"_id":None,"dups":{"$addToSet":{"$arrayElemAt":["$dups",1]}}}}],allowDiskUse=True)
    
    根据复制计数对dups阵列进行切片(在这里,我只为所有阵列增加了一个副本)


    我不知道它是否能回答主要问题,但对其他人来说,它是有用的。 1.使用findOne()方法查询重复行并将其存储为对象

    const User = db.User.findOne({_id:"duplicateid"});
    
    2.执行deleteMany()方法删除id为“duplicateid”的所有行

    3.插入存储在用户对象中的值

    db.User.insertOne(User);
    

    简单快速!!!!

    我的数据库有数百万条重复记录。@索姆纳特的答案不起作用,因为编写的解决方案对那些希望删除数百万条重复记录的人来说是有效的

    /**创建一个数组来存储所有重复记录ID*/
    重复变量=[];
    /**启动聚合管道*/
    db.collection.aggregate([
    {
    $match:{/**在此处添加任何筛选器。为筛选器键添加索引*/
    过滤键:{
    $exists:false
    }
    }
    },
    {
    $sort:{/**以您希望保留第一个元素的方式对其进行排序*/
    createdAt:-1
    }
    },
    {
    $group:{
    _身份证:{
    key1:“$key1”,key2:$key2”/**这些是定义重复项的键。在这里,对于key1和key2具有相同值的文档将被视为重复*/
    },
    DUP:{
    $push:{
    _id:“$\u id”
    }
    },
    计数:{
    $sum:1
    }
    }
    },
    {
    $match:{
    计数:{
    “$gt”:1
    }
    }
    }
    ],
    {
    allowDiskUse:对
    }).forEach(功能(文档){
    doc.dups.shift();
    doc.dups.forEach(函数(dupId){
    重复。推送(dupId.\u id);
    })
    })
    /**删除重复项*/
    var i,j,temparray,chunk=100000;
    
    对于(i=0,j=duplicates.length;i首先,您可以找到所有重复项并在数据库中删除这些重复项

    db.collection.aggregate([
        { "$group": { "_id": "$id", "count": { "$sum": 1 } } },
        { "$match": { "_id": { "$ne": null }, "count": { "$gt": 1 } } },
        { "$sort": { "count": -1 } },
        { "$project": { "name": "$_id", "_id": 0 } }
    ]).then(data => {
        var dr = data.map(d => d.name);
        console.log("duplicate Recods:: ", dr);
        db.collection.remove({ id: { $in: dr } }).then(removedD => {
            console.log("Removed duplicate Data:: ", removedD);
        })
    })
    

    名称本身不需要是唯一的。只有当名称和至少一个节点相同时,才会删除它吗?@user1188570它是复合的,所以两个fie
    db.User.deleteMany({_id:"duplicateid"});
    
    db.User.insertOne(User);
    
    db.collection.aggregate([
        { "$group": { "_id": "$id", "count": { "$sum": 1 } } },
        { "$match": { "_id": { "$ne": null }, "count": { "$gt": 1 } } },
        { "$sort": { "count": -1 } },
        { "$project": { "name": "$_id", "_id": 0 } }
    ]).then(data => {
        var dr = data.map(d => d.name);
        console.log("duplicate Recods:: ", dr);
        db.collection.remove({ id: { $in: dr } }).then(removedD => {
            console.log("Removed duplicate Data:: ", removedD);
        })
    })