MongoDB atomic “findOrCreate”: findOne, insertar si no existe, pero no actualizar

como dice el título, quiero realizar un find (uno) para un documento, por _id, y si no existe, hacer que se cree, luego si fue encontrado o fue creado, lo devolvió en la callback.

No quiero actualizarlo si existe, como he leído findAndModify. He visto muchas otras preguntas sobre Stackoverflow con respecto a esto, pero una vez más, no deseo actualizar nada.

No estoy seguro si al crear (de no existir), ESO en realidad es la actualización de la que todo el mundo está hablando, todo es muy confuso 🙁

A partir de MongoDB 2.4, ya no es necesario depender de un índice único (o de ninguna otra solución alternativa) para operaciones como findOrCreate atómico.

Esto es gracias al operador $setOnInsert nuevo en 2.4, que le permite especificar actualizaciones que solo deberían ocurrir al insertar documentos.

Esto, combinado con la opción upsert , significa que puede usar findAndModify para lograr una operación similar a findOrCreate atómica.

 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 }) 

Como $setOnInsert solo afecta a los documentos que se insertan, si se encuentra un documento existente, no se producirá ninguna modificación. Si no existe ningún documento, se levantará uno con el _id especificado, luego se realizará el conjunto de solo inserción. En ambos casos, el documento es devuelto.

Está un poco sucio, pero puedes simplemente insertarlo.

Asegúrese de que la clave tenga un índice único (si usa _id está bien, ya es único).

De esta forma, si el elemento ya está presente, devolverá una excepción que puede capturar.

Si no está presente, se insertará el nuevo documento.

Actualizado: una explicación detallada de esta técnica en la documentación de MongoDB

Esto es lo que hice (controlador Ruby MongoDB):

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

Se actualizará si existe, y lo insertará si no lo hace.

Con el último controlador (> versión 2), utilizará findOneAndUpdate ya que findAndModify quedó en desuso. El nuevo método toma 3 argumentos, el filter , el objeto de update (que contiene sus propiedades predeterminadas, que deben insertarse para un nuevo objeto) y las options donde debe especificar la operación de inserción.

Usando la syntax de la promesa, se ve así:

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