如何在收集中获取最新文档并删除其他文档

时间:2015-10-24 03:29:59

标签: mongodb mongodb-query aggregation-framework

我需要为每个用户收集一个集合中的最新文档(这里是通知),然后删除另一个。让我们假设我有这样的数据:

[
    { user: 1, time: ISODate("Mon, 14 Sep 2015 06:22:36 +0000"), msg: "message" },
    { user: 1, time: ISODate("Tue, 15 Sep 2015 06:22:36 +0000"), msg: "message" },
    { user: 1, time: ISODate("Fri, 23 Sep 2015 06:22:36 +0000"), msg: "message" },
    { user: 2, time: ISODate("Tue, 27 Sep 2015 06:22:36 +0000"), msg: "message" },
    { user: 2, time: ISODate("Wed, 28 Sep 2015 06:22:36 +0000"), msg: "message" },
    { user: 2, time: ISODate("Wed, 28 Sep 2015 07:33:16 +0000"), msg: "message" }
]

例如,我想获取最后2个通知并删除其他通知,因此user 1的结果应为:

[
    { user: 1, time: ISODate("Tue, 15 Sep 2015 06:22:36 +0000"), msg: "message" },
    { user: 1, time: ISODate("Fri, 23 Sep 2015 06:22:36 +0000"), msg: "message" }
]

数据将是这样的:

[
    { user: 1, time: ISODate("Tue, 15 Sep 2015 06:22:36 +0000"), msg: "message" },
    { user: 1, time: ISODate("Fri, 23 Sep 2015 06:22:36 +0000"), msg: "message" },
    { user: 2, time: ISODate("Tue, 27 Sep 2015 06:22:36 +0000"), msg: "message" },
    { user: 2, time: ISODate("Wed, 28 Sep 2015 06:22:36 +0000"), msg: "message" },
    { user: 2, time: ISODate("Wed, 28 Sep 2015 07:33:16 +0000"), msg: "message" }
]

和其他属于user 1的记录将被删除。 那么有效的方法是什么?

2 个答案:

答案 0 :(得分:1)

使用以下内容查找最后两个

 db.collection.find({user:1}).sort({time:-1}).limit(2)

使用以下内容删除除最后两个文档之外的所有文档

var i = 0;
var user_ids = [];
db.users.find({user:1},{_id:1}).sort({time:-1}).forEach(function(user) {
    if(i>1)
    user_ids[i] = user._id;
   i++;
});
db.users.remove({_id: {$in: user_ids}})

除了最后两个之外,没有直接method删除所有document。 但是,如果您一次只想删除一个document,那么您可以通过定义删除属性并将其设置为findAndModify来使用true函数

答案 1 :(得分:1)

我认为你的文件是这样的:

{ "_id" : ObjectId("562b38f9d6995d3311d9ddce"), "user" : 1, "time" : ISODate("2015-09-14T06:22:36Z"), "msg" : "message" }
{ "_id" : ObjectId("562b38f9d6995d3311d9ddcf"), "user" : 1, "time" : ISODate("2015-09-15T06:22:36Z"), "msg" : "message" }
{ "_id" : ObjectId("562b38f9d6995d3311d9ddd2"), "user" : 2, "time" : ISODate("2015-09-28T06:22:36Z"), "msg" : "message" }
{ "_id" : ObjectId("562b38f9d6995d3311d9ddd3"), "user" : 2, "time" : ISODate("2015-09-28T07:33:16Z"), "msg" : "message" }

MongoDB 3.2,您可以使用$slice运算符为每个用户仅获取最后两个文档

var bulkOp = db.collection.initializeOrderedBulkOp();
var count = 0;
db.collection.aggregate([ 
    { "$sort": { "user": 1, "time": -1 }}, 
    { "$group": { "_id": "$user", "times": { "$push": "$time" } }}, 
    { "$project": { 
        "times": { 
            "$slice": [ "$times", 2 ]
        }
     }}
]).forEach(function(doc) {
    bulkOp.find({ 
        "user": doc._id, 
        "time": { "$nin": doc.times }
    }).remove();
    count++;
    if(count % 100 === 0) {
        //Execute per 100 operations and re-init 
        bulkOp.execute();
        bulkOp = db.collection.initializeOrderedBulkOp();
    }
})

// Clean up queues
if(count > 0) {
    bulkOp.execute();
}

在MongoDB 3.2之前,您需要user $group您的文档,然后使用返回times数组的$push运算符。从那时起,您需要使用.forEach循环遍历聚合结果,然后在sort顺序中首先reverse times数组返回最后两次并使用.slice方法。然后,您可以使用"bulk"操作删除文档,以获得最高效率。当然,$nin运算符可以过滤掉旧文档。

var bulkOp = db.collection.initializeOrderedBulkOp();
var count = 0;
db.collection.aggregate([
    { "$group": {
        "_id": "$user", 
        "times": { "$push": "$time" } 
    } }
]).forEach(function(doc) { 
    var times = doc.times.sort(function(t1, t2) { 
        return t1 < t2 ? -1 :  (t1 > t2 ? 1 : 0); 
    }).reverse().slice(-2);
    bulkOp.find({ 
        "user": doc._id, 
        "time": { "$nin": times }
    }).remove(); 
    count++; 
    if(count % 100 === 0) {  
        //Execute per 100 operations and re-init   
        bulkOp.execute();     
        bulkOp = db.collection.initializeOrderedBulkOp(); 
    } 
})

// Clean up queues
if(count > 0) { 
    bulkOp.execute(); 
}