Alexander Zeitler
Alexander Zeitler

Reputation: 13099

MongoDb: Insert Document in collection only if collection has no newer document since point in time

I want to depict the following use case using MongoDb:

I want to read from a collection and memorize that particular point in time. When writing the next time to that collection, I want to not be able to write a new document, if another document has been added to that collection in between. Using a timestamp property on the documents would be ok.

Is this possible?

Upvotes: 1

Views: 359

Answers (3)

Mạnh Quyết Nguyễn
Mạnh Quyết Nguyễn

Reputation: 18235

One trick is use findAndModify

Assume at the time of reading, your most recent timestamp on a document is oldTimestamp:

db.collection.findAndModify({
    query: {timestamp: {$gt: oldTimestamp}},
    new: true, // Return modified / inserted document
    upsert: true, // Update if match found, insert otherwise
    update: {
         $setOnInsert: {..your document...}
    }
})

This will not insert your document if another document is inserted between your read and write operation.

However, this won't let you know that the document is inserted or not directly.

You should compare returned document with your proposed document to find that out.

In case using nodejs driver, the correct pattern should be:

collection.findAndModify(criteria[, sort[, update[, options]]], callback)

According to the example, our query should be:

db.collection('test').findAndModify(
  {timestamp: {$gt: oldTimestamp}}, // query, timestamp is a property of your document, often set as the created time
  [['timestamp','desc']],  // sort order
  {$setOnInsert: {..your document..}}, // replacement, replaces only the field "hi"
  {
      new: true,
      upsert: true
  }, // options
  function(err, object) {
      if (err){
          console.warn(err.message);  // returns error if no matching object found
      }else{
          console.dir(object);
      }
  });
});

Upvotes: 1

Nic Cottrell
Nic Cottrell

Reputation: 9665

Sounds like you'll need to do some sort of optimistic locking at the collection level. I understand you are writing new documents but never updating existing ones in this collection?

You could add an index on the timestamp field, and your application would need to track the latest version of this value. Then, before attempting a new write you could lookup the latest value from the collection with a query like

db.collection.find({}, {timestamp: 1, _id:0}).sort({timestamp:-1}).limit(1)

which would project just the maximum timestamp value using a covered query which is pretty efficient.

From that point on, it's up to your application logic to handle the 'conflict'.

Upvotes: 0

Sushim Mukul Dutta
Sushim Mukul Dutta

Reputation: 777

This can be achieved, using a timestamp property in every document. You can take a look at the Mongoose Pre Save path validation hook . Using this hook, you can write something like this.

YourSchema.path('timestamp').validate(function(value, done) {
  this.model(YourSchemaModelName).count({ timestamp: {$gt : value} }, function(err, count) {
    if (err) {
        return done(err);
    }
    // if count exists and not zero hence document is found with greater timestamp value
    done(!count);
});
}, 'Greater timestamp already exists');

Upvotes: 0

Related Questions