OPTIONS

Isolate Sequence of Operations

Overview

Write operations are atomic on the level of a single document: no single write operation can atomically affect more than one document or more than one collection.

When a single write operation modifies multiple documents, the operation as a whole is not atomic, and other operations may interleave. The modification of a single document, or record, is always atomic, even if the write operation modifies multiple sub-documents within the single record.

No other operations are atomic; however, you can isolate a single write operation that affects multiple documents using the isolation operator.

This document describes one method of updating documents only if the local copy of the document reflects the current state of the document in the database. In addition the following methods provide a way to manage isolated sequences of operations:

Update if Current

In this pattern, you will:

  • query for a document,
  • modify the fields in that document
  • and update the fields of a document only if the fields have not changed in the collection since the query.

Consider the following example in JavaScript which attempts to update the qty field of a document in the products collection:

Changed in version 2.6: The db.collection.update() method now returns a WriteResult() object that contains the status of the operation. Previous versions required an extra db.getLastErrorObj() method call.

var myCollection = db.products;
var myDocument = myCollection.findOne( { sku: 'abc123' } );

if (myDocument) {

   var oldQty = myDocument.qty;

   if (myDocument.qty < 10) {
       myDocument.qty *= 4;
   } else if ( myDocument.qty < 20 ) {
       myDocument.qty *= 3;
   } else {
       myDocument.qty *= 2;
   }

   var results = myCollection.update(
      {
        _id: myDocument._id,
        qty: oldQty
      },
      {
        $set: { qty: myDocument.qty }
      }
   );

   if ( results.hasWriteError() ) {
       print("unexpected error updating document: " + tojson( results ));
   } else if ( results.nMatched == 0 ) {
       print("No update: no matching document for { _id: " + myDocument._id + ", qty: " + oldQty + " }")
   }

}

Your application may require some modifications of this pattern, such as:

  • Use the entire document as the query in the update() operation, to generalize the operation and guarantee that the original document was not modified, rather than ensuring that as single field was not changed.
  • Add a version variable to the document that applications increment upon each update operation to the documents. Use this version variable in the query expression. You must be able to ensure that all clients that connect to your database obey this constraint.
  • Use $set in the update expression to modify only your fields and prevent overriding other fields.
  • Use one of the methods described in Create an Auto-Incrementing Sequence Field.