【问题标题】:Remove all Duplicates except the most recent document删除除最新文档之外的所有重复项
【发布时间】:2016-03-09 13:11:14
【问题描述】:

我想清除集合中特定字段的所有重复项。只留下最早的重复条目。

这是我的聚合查询,非常适合查找重复项:

db.History.aggregate([
  { $group: {
_id: { name: "$sessionId" },  
uniqueIds: { $addToSet: "$_id" },
count: { $sum: 1 } 
  } }, 
  { $match: { 
count: { $gte: 2 } 
  } },
  { $sort : { count : -1} }
 ],{ allowDiskUse:true,
  cursor:{}});

唯一的问题是我还需要执行删除查询并为每个重复项保留最年轻的条目(由字段“timeCreated”确定:

"timeCreated" : ISODate("2016-03-07T10:48:43.251+02:00")

我该怎么做?

【问题讨论】:

  • 请使用支持的输入和输出文档更新您的问题。
  • 是否有一个字段表示最年轻的条目?一种字段类型是日期,名为 updateDate?

标签: mongodb mongodb-query aggregation-framework


【解决方案1】:

就我个人而言,我会利用 ObjectId 值本身是“单调的”或因此“值不断增加”的事实,这意味着“最年轻的”或“最近的”将出现在自然排序列表的末尾.

因此,与其强制聚合管道进行排序,最合乎逻辑和最有效的做法是在处理每个响应时对每个文档返回的唯一 _id 值列表进行排序。

所以基本上是使用您必须找到的列表:

Remove Duplicates from MongoDB

实际上是我的答案(也是你本周第二个参考的人,但没有收到有用的投票!嗯!),它只是一个简单的.sort() 应用在光标内返回数组的迭代:

使用 _id 值

var bulk = db.History.initializeOrderedBulkOp(),
    count = 0;

// List "all" fields that make a document "unique" in the `_id`
// I am only listing some for example purposes to follow
db.History.aggregate([
    { "$group": {
        "_id": "$sessionId",
        "ids": { "$push": "$_id" }, // _id values are already unique, so $addToSet adds nothing
        "count": { "$sum": 1 }
    }},
    { "$match": { "count": { "$gt": 1 } } }
],{ "allowDiskUse": true}).forEach(function(doc) {
    doc.ids.sort().reverse();    // <-- this is the only real change
    doc.ids.shift();     // remove first match, which is now youngest
    bulk.find({ "_id": { "$in": doc.ids } }).remove();  // removes all $in list
    count++;

    // Execute 1 in 1000 and re-init
    if ( count % 1000 == 0 ) {
       bulk.execute();
       bulk = db.History.initializeOrderedBulkOp();
    }
});

if ( count % 1000 != 0 ) 
    bulk.execute();

使用特定字段

如果您“真的”设置为添加另一个日期值以确定哪个是最年轻的,那么只需首先添加到$push 中的数组,然后应用客户端排序功能。同样只是一个非常简单的更改:

var bulk = db.History.initializeOrderedBulkOp(),
    count = 0;

// List "all" fields that make a document "unique" in the `_id`
// I am only listing some for example purposes to follow
db.History.aggregate([
    { "$group": {
        "_id": "$sessionId",
        "ids": { "$push": { 
            "_id": "$_id",
            "created": "$timeCreated"
        }},
        "count": { "$sum": 1 }
    }},
    { "$match": { "count": { "$gt": 1 } } }
],{ "allowDiskUse": true}).forEach(function(doc) {
    doc.ids = doc.ids.sort(function(a,b) {   // sort dates and just return _id
        return a.created.valueOf() < a.created.valueOf()
    }).map(function(el) { return el._id });
    doc.ids.shift();     // remove first match, which is now youngest
    bulk.find({ "_id": { "$in": doc.ids } }).remove();  // removes all $in list
    count++;

    // Execute 1 in 1000 and re-init
    if ( count % 1000 == 0 ) {
       bulk.execute();
       bulk = db.History.initializeOrderedBulkOp();
    }
});

if ( count % 1000 != 0 ) 
    bulk.execute();

所以这是一个非常简单的过程,不会对用于识别重复项的原始过程进行“真正的”更改,然后删除除一个之外的所有重复项。

这里最好的方法总是让服务器完成查找重复项的工作,然后在客户端迭代游标时,您可以从返回的数组中计算出要保留的文档以及要保留的文档删除。

【讨论】:

  • 有一个错字:“a.created.valueOf()
  • 我使用的是 MongoDB 版本 3.2.13,但收到错误消息:“results_history2.default.initializeOrderedBulkOp 不是函数”我需要为此导入任何库吗?
  • 使用特定字段排序,在我的情况下日期不起作用。得到的 id 数组未排序
猜你喜欢
  • 1970-01-01
  • 2017-07-01
  • 2013-03-16
  • 2015-11-18
  • 1970-01-01
  • 2021-01-28
  • 1970-01-01
  • 2012-02-24
  • 2018-12-04
相关资源
最近更新 更多