2

I have been unable to find any documentation or examples on how to do this, but surely it's possible. Basically, I want to keep 20 of the most recent records. I want to order by a date (descending), skip 20, then remove the leftovers.

Alex311
  • 378
  • 3
  • 12

3 Answers3

3

You can do it in two steps -- 1, store the 20 most recent _ids and then 2, perform remove with $nin. Example code is below, which you can play with on my Saturn Fiddle. I use number, but you can obviously use some UNIX time stamp for your purpose.

// Welcome to SaturnAPI!
// Start collaborating with MongoDB fiddles and accomplish more.
// Start your code below these comments.

// Create a new collection
var Posts = new Mongo.Collection(null);

//Insert some data
Posts.insert({
  number: 1,
  author: "Saturn Sam", 
  message: "Hello!"
});
Posts.insert({
  number: 2,
  author: "Saturn Sam2", 
  message: "Hello!"
});
Posts.insert({
  number: 3,
  author: "Saturn Sam3", 
  message: "Hello!"
});

var recent = Posts.find({number: {$gte: 2}}).fetch().map(function (thisPost) {
  return thisPost._id;
});

// Remove all but recent
Posts.remove({_id: {$nin: recent}});

// Returns all remaining records
Posts.find({}).fetch()
FullStack
  • 5,902
  • 4
  • 43
  • 77
  • Thanks for the reply! I'm hoping for a more atomic approach. To go non-atomically, I would think that it would be more efficient to do a sorted find with a skip to get the date of the 20th document, then remove everything older than that. I suppose there isn't a single command to do this? – Alex311 Sep 15 '15 at 20:13
  • For atomicity, the closest solution I can recommend is altering your remove query to use `$gt` or `$lt` instead of `sort` and `skip`. This could work if you are sorting by timestamps, etc. – FullStack Sep 16 '15 at 03:49
1

I will suggest to go for capped collection with limit of 20 documents. It will delete last on insertion of new one. Hope it will help

  • 1
    This would work great, however, I made my question simple and left out the fact that that documents in this collection is grouped. That is, I want to maintain a max size of 20 documents for each subID, which capped collections cannot do. – Alex311 Sep 15 '15 at 22:09
1

As I mentioned in my comment to the capped collection answer, I have documents in my collection that I want grouped by a certain ID, then limited to the 20 most recent. This isn't the perfect answer since it doesn't atomically remove documents, but it seems to be the best option I have.

I find the 21st document in the sorted results. If a 21st document exists (because I could have only 20 or less), then I remove it and everything older than it for my category and subcategory.

_id is an ObjectId.

db.logs.find({'catID': catID, 'subID': subID}, {_id: 1}).sort({'_id': -1}).skip(20).limit(1, function (err, docs) {
    if(!err && docs.length) {
        // If there was a 21st _id, remove it and all logs older than it for that category and subcategory
        db.logs.remove({'catID': catID, 'subID': subID, '_id': {$lte: mongojs.ObjectId(docs[0]._id)}}, callback);
        return;
    }
    callback(err, docs);
});
Alex311
  • 378
  • 3
  • 12