MongoDB atomik "findOrCreate": findOne, yoksa ekle, ancak güncelleme


114

başlığın da dediği gibi, bir belge için _id ile bir bulma (bir) gerçekleştirmek istiyorum ve yoksa, yarattınız, sonra bulunmuş mu yoksa yaratılmış mı, geri aramada döndürülsün.

FindAndModify'ın yaptığını okuduğum gibi, varsa onu güncellemek istemiyorum. Stackoverflow'da bununla ilgili birçok başka soru gördüm, ancak yine de hiçbir şeyi güncellemek istemiyorum.

Yaratarak (varolmadan), BU aslında herkesin bahsettiği güncelleme olup olmadığından emin değilim, hepsi çok kafa karıştırıcı :(

Yanıtlar:


192

MongoDB 2.4 ile başlayarak, atomik findOrCreatebenzeri işlemler için artık benzersiz bir dizine (veya başka herhangi bir geçici çözüme) güvenmek gerekmiyor .

Bu, yalnızca belge eklerken olması gereken güncellemeleri belirlemenize olanak tanıyan 2.4'e yeni olan $setOnInsertoperatör sayesinde olur.

Bu upsertseçenekle birleştirildiğinde, findAndModifyatom findOrCreatebenzeri bir işlem elde etmek için kullanabileceğiniz anlamına gelir .

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

Gibi $setOnInsertsadece eklenmekte olan belgeleri etkiler varolan belge bulunursa, hiçbir değişiklik meydana gelecektir. Belge yoksa, belirtilen _id ile birini yükseltecek, ardından yalnızca ekleme setini gerçekleştirecektir. Her iki durumda da belge iade edilir.


3
@Gank, düğüm için mongodb yerel sürücüsünü kullanıyorsanız, sözdizimi daha çok olacaktır collection.findAndModify({_id:'theId'}, <your sort opts>, {$setOnInsert:{foo: 'bar'}}, {new:true, upsert:true}, callback). Belgelere
numbers1311407

7
Belgenin güncellenip güncellenmediğini bilmenin bir yolu var mı?
doom

3
Yukarıdaki ( ) sorgunun bir belgeyi ekleyip eklemediğini (yükselterek) kontrol etmek istiyorsanız , kullanmayı düşünmelisiniz . Doküman zaten mevcutsa , doküman eklenmişse geri döner . db.collection.findAndModify({query: {_id: "some potentially existing id"}, update: {$setOnInsert: {foo: "bar"}}, new: true, upsert: true})db.collection.updateOne({_id: "some potentially existing id"}, {$setOnInsert: {foo: "bar"}}, {upsert: true}){"acknowledged": true, "matchedCount": 0, "modifiedCount": 0, "upsertedId": ObjectId("for newly inserted one")}{"acknowledged": true, "matchedCount": 1, "modifiedCount": 0}
Константин Ван

8
görünüyor lezzet kullanımdan kaldırılmış olması findOneAndUpdate, findOneAndReplaceya dafindOneAndDelete
Ravshan Samandarov

5
Yine de burada dikkatli olunması gerekiyor. Bu yalnızca findAndModify / findOneAndUpdate / updateOne seçicisi bir belgeyi _id ile benzersiz bir şekilde tanımlarsa çalışır. Aksi takdirde, yükseltme sunucuda bir sorguya ve bir güncelleme / eke bölünür. Güncelleme hala atomik olacaktır. Ancak sorgu ve güncelleme birlikte atomik olarak yürütülmeyecektir.
Anon

15

Sürücü Sürümleri> 2

En son sürücüyü (> sürüm 2) kullanarak , kullanımdan kaldırıldığı gibi findOneAndUpdate'i kullanacaksınız findAndModify. Yeni yöntem 3 argüman alır filter, updatenesne (varsayılan özelliklerinizi içeren, yeni bir nesne için eklenmesi gereken) ve optionsyükseltme işlemini belirtmeniz gereken yer.

Promise sözdizimini kullanarak şöyle görünür:

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

const newOrUpdatedDocument = result.value;

Teşekkürler, returnOriginalolmalı returnNewDocumentbence - docs.mongodb.com/manual/reference/method/...
Dominic

Nevermind, düğüm sürücüsü bunu farklı şekilde uyguladı ve cevabınız doğru
Dominic

6

Biraz kirli ama sadece yerleştirebilirsiniz.

Anahtarın üzerinde benzersiz bir dizine sahip olduğundan emin olun (_id kullanırsanız sorun yok, zaten benzersizdir).

Bu şekilde, eğer öğe zaten mevcutsa, yakalayabileceğiniz bir istisna döndürecektir.

Mevcut değilse, yeni belge eklenecektir.

Güncellenmiş: MongoDB Belgelerinde bu tekniğin ayrıntılı bir açıklaması


tamam, bu iyi bir fikir, ancak önceden var olan değer için bir hata döndürecektir, ancak değerin kendisi DEĞİL, değil mi?
Discipol

3
Bu aslında, izole edilmiş işlem dizileri için önerilen çözümlerden biridir (muhtemelen bulunamazsa bulun ve yaratın) docs.mongodb.org/manual/tutorial/isolate-sequence-of-operations
numbers1311407

@Discipol Bir dizi atomik işlem yapmak istiyorsanız, önce Belgeyi kilitlemeli, ardından değiştirmeli ve sonunda serbest bırakmalısınız. Bu, daha fazla sorgu gerektirir, ancak en iyi durum senaryosu için optimize edebilir ve çoğu zaman yalnızca 1-2 sorgu yapabilirsiniz. bkz: docs.mongodb.org/manual/tutorial/perform-two-phase-commits
Madarco

1

İşte yaptığım şey (Ruby MongoDB sürücüsü):

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

Varsa günceller, yoksa ekler.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.