删除mongodb上的重复项

时间:2017-05-18 17:51:35

标签: mongodb mongodb-query aggregation-framework

我想删除robomongo上的重复项,我的版本3.0.12所以我不能使用DropDups,

{
    "_id" : ObjectId("id"),
    "Name" : "No One",
    "SituationDate" : "18-03-2017",
    "Situation" : "ACTIVE",
    "Region" : "13 REGION",
    "RegisterNumber" : "7649",
    "Version" : "20170517"
}

RegisterNumber应该是唯一的,所以我想删除RegisterNumber的重复项。

编辑:我刚刚发现来自不同地区的人可以拥有相同的registerNumber ...我怎样才能删除那些同时具有RegisterNumber和Region的人

解决方案: 以下是@Neil Lunn给出的小修改解决方案,我在一个名为TEST的集合中对其进行了测试并且有效:

var bulk = db.getCollection('TEST').initializeOrderedBulkOp();
var count = 0;

db.getCollection('TEST').aggregate([
  // Group on unique value storing _id values to array and count 
  { "$group": {
    "_id": { RegisterNumber: "$RegisterNumber", Region: "$Region" },
    "ids": { "$push": "$_id" },
    "count": { "$sum": 1 }      
  }},
  // Only return things that matched more than once. i.e a duplicate
  { "$match": { "count": { "$gt": 1 } } }
]).forEach(function(doc) {
  var keep = doc.ids.shift();     // takes the first _id from the array

  bulk.find({ "_id": { "$in": doc.ids }}).remove(); // remove all remaining _id matches
  count++;

  if ( count % 500 == 0 ) {  // only actually write per 500 operations
      bulk.execute();
      bulk = db.getCollection('TEST').initializeOrderedBulkOp();  // re-init after execute
  }
});

// Clear any queued operations
if ( count % 500 != 0 )
    bulk.execute();

1 个答案:

答案 0 :(得分:2)

如果您准备简单地丢弃所有其他重复项,那么您基本上希望.aggregate()以便收集具有相同RegisterNumber值的文档并删除除第一个匹配项以外的所有其他文档。< / p>

MongoDB 3.0.x缺少一些现代帮助程序,但.aggregate()为进程大型结果集返回游标的基础知识以及"bulk operations"对于写入性能的存在仍然存在:

var bulk = db.collection.initializeOrderedBulkOp();
var count = 0;

db.collection.aggregate([
  // Group on unique value storing _id values to array and count 
  { "$group": {
    "_id": "$RegisterNumber",
    "ids": { "$push": "$_id" },
    "count": { "$sum": 1 }      
  }},
  // Only return things that matched more than once. i.e a duplicate
  { "$match": { "count": { "$gt": 1 } } }
]).forEach(function(doc) {
  var keep = doc.ids.shift();     // takes the first _id from the array

  bulk.find({ "_id": { "$in": doc.ids }}).remove(); // remove all remaining _id matches
  count++;

  if ( count % 500 == 0 ) {  // only actually write per 500 operations
      bulk.execute();
      bulk = db.collection.initializeOrderedBulkOp();  // re-init after execute
  }
});

// Clear any queued operations
if ( count % 500 != 0 )
    bulk.execute();

在更现代的版本(3.2及更高版本)中,最好使用bulkWrite()代替。请注意,这是一个客户端库&#39;事情,同样的&#34;批量&#34;上面显示的方法实际上被称为&#34;引擎盖下#34;:

var ops = [];

db.collection.aggregate([
  { "$group": {
    "_id": "$RegisterNumber",
    "ids": { "$push": "$id" },
    "count": { "$sum": 1 }      
  }},
  { "$match": { "count": { "$gt": 1 } } }
]).forEach( doc => {

  var keep = doc.ids.shift();

  ops = [
    ...ops,
    {
      "deleteMany": { "filter": { "_id": { "$in": doc.ids } } }
    }
  ];

  if (ops.length >= 500) {
    db.collection.bulkWrite(ops);
    ops = [];
  }
});

if (ops.length > 0)
  db.collection.bulkWrite(ops);

所以$group通过$RegisterNumber值将所有内容拉到一起,并将匹配的文档_id值收集到一个数组中。您可以使用$sum保持这种情况发生的次数。

然后过滤掉任何只有1的文件,因为这些文件显然不重复。

传递给循环,您会删除收集的_id密钥中.shift()的第一次出现,只留下其他&#34;重复的&#34;在阵列中。

这些传递给&#34;删除&#34;使用$in作为&#34;列表&#34;要匹配和删除的文件。

如果您需要更复杂的内容(例如合并其他重复文档中的详细信息),则此过程通常是相同的,只是在执行类似转换&#34;的情况时您可能需要更多关注。独特的关键&#34;因此,在将更改写入要修改的文档之前,首先要删除重复项。

无论如何,聚合将突出显示实际上是&#34;重复&#34;的文档。剩下的处理逻辑基于您在识别它们时实际想要对该信息做什么。