2015-10-24 3 views
3

Мне нужно получить последний документ в коллекции (здесь есть уведомления) для каждого пользователя и удалить другой. Давайте предположим, у меня есть данные, как это:Как получить последние документы в коллекции и удалить другие

[ 
    { 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, будут удалены. Итак, каков эффективный способ сделать это?

+0

Если я понимаю, что вы хотите обновить документы в своей коллекции и оставить только последние два для каждого пользователя. правильно? – styvane

+0

@ пользователь3100115 да, эта. – windyzboy

ответ

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 вам необходимо указать $group ваш документ user, а затем использовать оператор $push, который возвращает массив times. С этого момента вам нужно будет выполнить циклический результат агрегации с помощью цикла .forEach, а затем вернуть последние два раза первым sort в ваш массив times в reverse и использовать метод .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(); 
} 
+1

После того, как вы попытались записать около 3M записей, этот способ намного лучше. Большое вам спасибо: D – windyzboy

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

+0

Ну, я знаю, как их получить, но я не знаю, как удалить другую. – windyzboy

+0

вы хотите удалить весь документ после поиска или за исключением последних двух –

+0

Я хочу удалить документы, принадлежащие 'user 1', за исключением последних двух. Не удалять все документы в коллекции. – windyzboy