MongoDBアトミック「findOrCreate」:findOne、存在しない場合は挿入しますが、更新しません


114

タイトルが示すように、_idによってドキュメントの検索(1つ)を実行し、存在しない場合は作成し、それが見つかったか作成されたかに関係なく、コールバックで返します。

findAndModifyを読んだように、存在する場合は更新したくありません。これに関してStackoverflowで他の多くの質問を見てきましたが、何も更新したくありません。

(存在しない)を作成することによって、それが実際に誰もが話している更新であるかどうかはわかりませんが、それはすべて混乱しています:(

回答:


192

MongoDB 2.4以降、アトミックのfindOrCreateような操作で一意のインデックス(またはその他の回避策)に依存する必要がなくなりました。

これは、おかげでオペレータドキュメントを挿入するときにのみ起こるはずの更新を指定することができます2.4に新しいです、。$setOnInsert

これをupsertオプションと組み合わせると、を使用findAndModifyして、アトミックの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
})

$setOnInsertのみ挿入された文書に影響し、既存の文書が発見された場合、何も変更は行われません。ドキュメントが存在しない場合は、指定された_idでドキュメントをアップサートし、挿入のみのセットを実行します。どちらの場合も、ドキュメントが返されます。


3
@Gankノードにmongodbネイティブドライバーを使用している場合、構文はより似たものになりcollection.findAndModify({_id:'theId'}, <your sort opts>, {$setOnInsert:{foo: 'bar'}}, {new:true, upsert:true}, callback)ます。ドキュメントを
番号1311407

7
ドキュメントが更新または挿入されたかどうかを確認する方法はありますか?
2014

3
あなたがしたい場合かどうかをチェックする(上記のクエリ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
味で廃止されるように見えるfindOneAndUpdatefindOneAndReplaceまたはfindOneAndDelete
ラフシャンSamandarov

5
ただし、ここでは注意が必要です。これは、findAndModify / findOneAndUpdate / updateOneのセレクターが_idによって1つのドキュメントを一意に識別する場合にのみ機能します。そうでない場合、アップサートはサーバー上でクエリと更新/挿入に分割されます。更新は引き続きアトミックです。ただし、クエリと更新は一緒にアトミックに実行されません。
Anon 2017

14

ドライバーのバージョン> 2

最新のドライバー(>バージョン2)を使用して、廃止予定のfindOneAndUpdateを使用しますfindAndModify。新しい方法は3つの引数を取りfilterupdate(新しいオブジェクトの挿入すべきデフォルトのプロパティが含まれている、)オブジェクト、そしてoptionsどこにアップサート操作を指定する必要があります。

promise構文を使用すると、次のようになります。

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

const newOrUpdatedDocument = result.value;

おかげで、returnOriginalしなければならないreturnNewDocumentと思う- docs.mongodb.com/manual/reference/method/...
ドミニク


6

少し汚れていますが、挿入するだけです。

キーに一意のインデックスが付いていることを確認してください(_idを使用しても問題ありませんが、すでに一意です)。

このようにして、要素がすでに存在する場合は、キャッチできる例外を返します。

存在しない場合は、新しいドキュメントが挿入されます。

更新:MongoDBドキュメントでのこの手法の詳細な説明


OK、それは良い考えですが、既存の値の場合はエラーを返しますが、値自体は返しません。
Discipol 2013年

3
これは実際には、孤立した一連の操作に推奨されるソリューションの1つです(おそらく見つからない場合は見つけて作成します)docs.mongodb.org/manual/tutorial/isolate-sequence-of-operations
numbers1311407

@Discipol一連のアトミック操作を実行する場合は、最初にドキュメントをロックしてから変更し、最後に解放する必要があります。これにはより多くのクエリが必要になりますが、ベストケースのシナリオに最適化して、ほとんどの場合1〜2回のクエリしか実行できません。参照:docs.mongodb.org/manual/tutorial/perform-two-phase-commits
Madarco

1

これが私がやったことです(Ruby MongoDBドライバー):

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

存在する場合は更新し、存在しない場合は挿入します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.