MongoDB atomic “findOrCreate”: findOne, insira se inexistente, mas não atualiza

como o título diz, eu quero executar um find (um) para um documento, por _id, e se não existir, ele foi criado, então se ele foi encontrado ou foi criado, ele retornou no callback.

Eu não quero atualizá-lo se existir, como eu li o findAndModify. Já vi muitas outras perguntas no Stackoverflow sobre isso, mas, novamente, não quero atualizar nada.

Eu não tenho certeza se criando (de não existir), essa é realmente a atualização que todo mundo está falando, é tudo tão confuso 🙁

Começando com o MongoDB 2.4, não é mais necessário confiar em um índice exclusivo (ou qualquer outra solução alternativa) para operações semelhantes findOrCreate atômicas.

Isso é graças ao operador $setOnInsert , novo para o 2.4, que permite que você especifique as atualizações que devem ocorrer somente ao inserir documentos.

Isso, combinado com a opção upsert , significa que você pode usar o findAndModify para obter uma operação do tipo 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 afeta apenas os documentos que estão sendo inseridos, se um documento existente for encontrado, nenhuma modificação ocorrerá. Se nenhum documento existir, ele fará um backup com o _id especificado e executará o conjunto somente de inserção. Nos dois casos, o documento é retornado.

É um pouco sujo, mas você pode simplesmente inseri-lo.

Certifique-se de que a chave tenha um índice exclusivo (se você usar o _id, tudo bem, ele já é único).

Dessa forma, se o elemento já estiver presente, ele retornará uma exceção que você pode capturar.

Se não estiver presente, o novo documento será inserido.

Atualizado: uma explicação detalhada desta técnica na documentação do MongoDB

Aqui está o que eu fiz (driver do Ruby MongoDB):

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

Ele irá atualizá-lo se existir e inseri-lo se não existir.

Usando o driver mais recente (> versão 2), você usará findOneAndUpdate como findAndModify foi preterido. O novo método usa 3 argumentos, o filter , o object de update (que contém suas propriedades padrão, que devem ser inseridas para um novo object) e as options que você deve especificar a operação upsert.

Usando a syntax da promise, é assim:

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