MongoDB atomico “findOrCreate”: findOne, inserire se inesistente, ma non aggiornare

come dice il titolo, voglio eseguire una ricerca (uno) per un documento, per _id, e se non esiste, averlo creato, quindi se è stato trovato o è stato creato, farlo restituire nel callback.

Non voglio aggiornarlo se esiste, come ho letto findAndModify. Ho visto molte altre domande su Stackoverflow riguardo a questo, ma ancora una volta, non desidero aggiornare nulla.

Non sono sicuro se creando (di non esistente), QUESTO è in realtà l’aggiornamento di cui tutti parlano, è tutto così confuso 🙁

A partire da MongoDB 2.4, non è più necessario fare affidamento su un indice univoco (o su qualsiasi altra soluzione alternativa) per operazioni di tipo findOrCreate atomico.

Questo grazie all’operatore $setOnInsert nuovo a 2.4, che consente di specificare gli aggiornamenti che dovrebbero verificarsi solo quando si inseriscono documenti.

Questo, combinato con l’opzione upsert , significa che è ansible utilizzare findAndModify per ottenere un’operazione atomica di tipo findOrCreate .

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

Poiché $setOnInsert effetto solo sui documenti che vengono inseriti, se viene trovato un documento esistente, non si verificherà alcuna modifica. Se non esiste alcun documento, ne effettuerà uno in avanti con l’id specificato, quindi eseguirà il set solo dell’inserto. In entrambi i casi, il documento viene restituito.

È un po ‘sporco, ma puoi semplicemente inserirlo.

Assicurati che la chiave abbia un indice univoco (se usi _id è ok, è già univoco).

In questo modo se l’elemento è già presente restituirà un’eccezione che puoi catturare.

Se non è presente, verrà inserito il nuovo documento.

Aggiornato: una spiegazione dettagliata di questa tecnica sulla documentazione MongoDB

Ecco cosa ho fatto (driver Ruby MongoDB):

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

Lo aggiornerà se esiste e lo inserirà in caso contrario.

Usando il driver più recente (> versione 2), userete findOneAndUpdate come findAndModify stato deprecato. Il nuovo metodo accetta 3 argomenti, il filter , l’object di update (che contiene le proprietà predefinite, che devono essere inseriti per un nuovo object) e le options cui è necessario specificare l’operazione di upsert.

Usando la syntax promise, sembra che questo:

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