MongoDB atomic "findOrCreate": findOne, insert if nonexistent, but do not update

mongodb atomic increment
mongodb findandmodify atomic
mongodb findoneandupdate atomic
mongodb transactions c#
mongodb transactions example
mongodb transactions java
mongodb lock document
mongodb update

as the title says, I want to perform a find (one) for a document, by _id, and if doesn't exist, have it created, then whether it was found or was created, have it returned in the callback.

I don't want to update it if it exists, as I've read findAndModify does. I have seen many other questions on Stackoverflow regarding this but again, don't wish to update anything.

I am unsure if by creating (of not existing), THAT is actually the update everyone is talking about, it's all so confuzzling :(

Beginning with MongoDB 2.4, it's no longer necessary to rely on a unique index (or any other workaround) for atomic findOrCreate like operations.

This is thanks to the $setOnInsert operator new to 2.4, which allows you to specify updates which should only happen when inserting documents.

This, combined with the upsert option, means you can use findAndModify to achieve an atomic findOrCreate-like operation.

db.collection.findAndModify({
  query: { _id: "some potentially existing id" },
  update: {
    $setOnInsert: { foo: "bar" }
  },
  new: true,   // return new doc if one is upserted
  upsert: true // insert the document if it does not exist
})

As $setOnInsert only affects documents being inserted, if an existing document is found, no modification will occur. If no document exists, it will upsert one with the specified _id, then perform the insert only set. In both cases, the document is returned.

Model Data for Atomic Operations, In MongoDB, a write operation on a single document is atomic. For fields that must be updated together, embedding the fields within the same document  In MongoDB, a write operation is atomic on the level of a single document, even if the operation modifies multiple embedded documents within a single document. Multi-Document Transactions ¶ When a single write operation (e.g. db.collection.updateMany() ) modifies multiple documents, the modification of each document is atomic, but the operation as a whole is not atomic.

Driver Versions > 2

Using the latest driver (> version 2), you'll use findOneAndUpdate as findAndModify was deprecated. The new method takes 3 arguments, the filter, the update object (which contains your default properties, that should be inserted for a new object), and options where you have to specify the upsert operation.

Using the promise syntax, it looks like this:

const result = await collection.findOneAndUpdate(
  { _id: new ObjectId(id) },
  {
    $setOnInsert: { foo: "bar" },
  },
  {
    returnOriginal: false,
    upsert: true,
  }
);

const newOrUpdatedDocument = result.value;

MongoDB - Atomic Operations, MongoDB - Atomic Operations - MongoDB does not support multi-document atomic transactions. However, it does provide atomic operations on a single  In MongoDB, a write operation on a single document is atomic. For fields that must be updated together, embedding the fields within the same document ensures that the fields can be updated atomically.

Its a bit dirty, but you can just insert it.

Be sure that the key has a unique index on it (if you use the _id it's ok, it's already unique).

In this way if the element is already present it will return an exception that you can catch.

If it isn't present, the new document will be inserted.

Updated: a detailed explanation of this technique on the MongoDB Documentation

How to perform Atomic Operations on MongoDB?, Mongo is the leader when it comes to NoSQL. There are plenty of amazing features of MongoDB, and one of them are atomic operations. MongoDB does not support multi-document atomic transactions. However, it does provide atomic operations on a single document. However, it does provide atomic operations on a single document. So if a document has hundred fields the update statement will either update all the fields or none, hence maintaining atomicity at the document-level.

Here's what I did (Ruby MongoDB driver):

$db[:tags].update_one({:tag => 'flat'}, {'$set' => {:tag => 'earth' }}, { :upsert => true })}

It will update it if it exists, and insert it if it doesn't.

Atomicity, isolation & concurrency in MongoDB, MongoDB write operations are atomic, only at the level of a single document. If you're modifying multiple subdocuments inside a document the  We can explain atomic operations in MongoDB clearly with the use of ACID, a.k.a. Atomicity, Consistency, Isolation, and Durability. Here is a simple rule of atomicity for every single operation, “either all or none.”

Learn How to Use Atomic Operations in MongoDB, MongoDB provides atomic operations only on single documents. E.g., if a document has fifty fields then the update statement will either update all  MongoDB does not help in multi-document atomic transactions. Nonetheless, it does give atomic operations on a single document. So if a document has hundred fields the update query will either update all the fields or none, thus keeping up atomicity at document level.

is mongo update ($set) on a single document atomic, Yes, all write operations with MongoDB are atomic at the level of a single document. The key difference between update and findAnyModify is  MongoDB write operations are atomic, only at the level of a single document. If you’re modifying multiple subdocuments inside a document the operation is still atomic, but if you’re modifying multiple documents, the operation is not atomic.

Chapter 6. Updates, atomic operations, and deletes, Updates, atomic operations, and deletes. In this chapter. Updating documents; Processing documents atomically; Category hierarchies and inventory management. What you are trying to achieve would be a transaction operation in mongodb, which is not atomic since it involves more than one document. You could very easily though have the city referenced from each of the person documents rather than have each city reference each person.

Comments
  • @Gank if you're using the mongodb native driver for node the syntax would be more like collection.findAndModify({_id:'theId'}, <your sort opts>, {$setOnInsert:{foo: 'bar'}}, {new:true, upsert:true}, callback). See the docs
  • Is there a way to know if the document has been updated or inserted ?
  • If you want to check if the query above(db.collection.findAndModify({query: {_id: "some potentially existing id"}, update: {$setOnInsert: {foo: "bar"}}, new: true, upsert: true})) insert(upsert)ed a document, you should consider using db.collection.updateOne({_id: "some potentially existing id"}, {$setOnInsert: {foo: "bar"}}, {upsert: true}). It returns {"acknowledged": true, "matchedCount": 0, "modifiedCount": 0, "upsertedId": ObjectId("for newly inserted one")} if the doc inserted, {"acknowledged": true, "matchedCount": 1, "modifiedCount": 0} if the doc already exists.
  • seems to be deprecated in flavor of findOneAndUpdate, findOneAndReplace or findOneAndDelete
  • One needs to be careful here, though. This only works if the selector of the findAndModify/findOneAndUpdate/updateOne uniquely identifies one document by _id. Otherwise the upsert is split up on the server into a query and an update/insert. The update will still be atomic. But the query and the update together will not be executed atomically.
  • Thanks, returnOriginal should be returnNewDocument I think - docs.mongodb.com/manual/reference/method/…
  • Nevermind, the node driver implemented it differently and your answer is correct
  • ok, thats a good idea, but for the pre-existing value it will return an error but NOT the value itself, right?
  • This is actually one of the recommended solutions for isolated sequences of operations (find then create if not found, presumably) docs.mongodb.org/manual/tutorial/isolate-sequence-of-operations
  • @Discipol if you want to do a set of atomic operations, you should first lock the Document, then modify it, and at the end release it. This will require more queries, but you can optimize for the best case scenario and do only 1-2 queries most of the times. see: docs.mongodb.org/manual/tutorial/perform-two-phase-commits