web-dev-qa-db-ja.com

MongoDB-存在しない場合は挿入、そうでない場合はスキップ

条件付きでMongoに挿入することは可能ですか?

//Pseudo code

Bulk Insert Item :

If Key exists
    Skip, don't throw error
If key does not exist
    Add item

単一の挿入を行うと、エラーが返されるかコレクションに挿入される場合がありますが、bulkで可能ですか?

16
Basit Anwer

ここでは、処理方法に応じて2つの実際の選択肢があります。

  1. psert MongoDBの機能を使用して、キーデータが存在する場合、本質的に「ルックアップ」します。そうでない場合は、データを [_$setOnInsert_ に渡すだけで、それ以外は何もしません。

  2. 「UnOrdered」操作を一括で使用します。エラーが返された場合でも、更新のバッチ全体が続行されますが、エラーレポートはそれだけであり、エラーでないものはすべてコミットされます。

全体の例:

_var async = require('async'),
    mongoose = require('mongoose'),
    Schema = mongoose.Schema;

var testSchema = new Schema({
  "_id": Number,
  "name": String
},{ "_id": false });

var Test = mongoose.model('Test',testSchema,'test');

mongoose.connect('mongodb://localhost/test');

var data = [
  { "_id": 1, "name": "One" },
  { "_id": 1, "name": "Another" },
  { "_id": 2, "name": "Two" }
];

async.series(
  [
    // Start fresh
    function(callback) {
      Test.remove({},callback);
    },

    // Ordered will fail on error. Upserts never fail!
    function(callback) {
      var bulk = Test.collection.initializeOrderedBulkOp();
      data.forEach(function(item) {
        bulk.find({ "_id": item._id }).upsert().updateOne({
          "$setOnInsert": { "name": item.name }
        });
      });
      bulk.execute(callback);
    },

    // All as expected
    function(callback) {
      Test.find().exec(function(err,docs) {
        console.log(docs)
        callback(err);
      });
    },


    // Start again
    function(callback) {
      Test.remove({},callback);
    },

    // Unordered will just continue on error and record an error
    function(callback) {
      var bulk = Test.collection.initializeUnorderedBulkOp();
      data.forEach(function(item) {
        bulk.insert(item);
      });
      bulk.execute(function(err,result) {
        callback(); // so what! Could not care about errors
      });
    },


    // Still processed the whole batch
    function(callback) {
      Test.find().exec(function(err,docs) {
        console.log(docs)
        callback(err);
      });
    }
  ],
  function(err) {
    if (err) throw err;
    mongoose.disconnect();
  }
);
_

現在のドライバーの「変更されたアクション」は、.execute()willの結果応答がスローされるエラーオブジェクトを返すことに注意してください。以前のリリースでは「Un-順序付けられた」操作。

これは、コードが単独で返されるerrに決して依存しないことが不可欠であり、エラーの完全な分類のために、返されたresultを代わりに使用する必要があります。

それでも、順序付けられていない場合、エラーがいくつ発生しても、バッチは最後まで続行されます。エラーではないものは通常どおりコミットされます。

これは、実際には「シーケンスが重要」です。その場合、「順序付けられた」操作が必要であり、「アップサート」を使用することによってのみ重複キーを回避できます。それ以外の場合は「順不同」を使用しますが、エラーの戻り値と実際の意味に注意してください。

また、_.collection_を使用して基本ドライバーから基になるコレクションオブジェクトを取得し、「一括」操作を有効にする場合は、必ず「some」mongooseメソッドが常に最初に呼び出されていることを確認してください。

これがないと、mongooseメソッドで処理されるため、ネイティブドライバメソッドを使用したデータベースへの接続が保証されないため、接続がないため操作が失敗します。

最初にmongooseメソッドを「起動」する代わりに、接続のイベントリスナーでアプリのロジックをラップします。

_mongoose.connection.on("open",function(err) {
    // app logic in here
})
_
22
Blakes Seven

既に述べたように、updateオプションをtrueに設定してupsertコマンドを使用すると、「存在しない場合に挿入」を実現できます。 3.x node.jsドライバーでこれを行う方法は次のとおりです。

let ops = [];
ops.Push({ updateOne: { filter: {key:"value1"}, update: {} }, { upsert:true } });
ops.Push({ updateOne: { filter: {key:"value2"}, update: { $set:{/*...*/} } }, { upsert:true } });
ops.Push({ updateOne: { filter: {key:"value3"}, update: { { $setOnInsert:{/*...*/} } } }, { upsert:true } });
// < add more ops here >
await db.collection("my-collection").bulkWrite(ops, {ordered:false});

filterがゼロの結果を返す場合、フィルター条件と$set更新(存在する場合)を使用して新しいドキュメントが作成されます。 $setOnInsertを使用する場合、更新は新しいドキュメントにのみ適用されます。

私の状況では便利だったので、この例を投稿しました。 db.collection.bulkWrite のドキュメントの詳細情報。

4
user993683