質問

Mongodbでは、別のフィールドからの値を使用してフィールドの値を更新することは可能ですか?同等のSQLは次のようなものです。

UPDATE Person SET Name = FirstName + ' ' + LastName

そして、mongodb擬似コードは次のとおりです。

db.person.update( {}, { $set : { name : firstName + ' ' + lastName } );
役に立ちましたか?

解決

これを行う最良の方法は、更新ドキュメントと updateOne, updateMany また update 収集方法。後者は、すべてではないにしても、ほとんどの言語ドライバーで廃止されたことに注意してください。

Mongodb 4.2+

バージョン4.2も紹介しました $set エイリアスであるパイプラインステージオペレーター $addFields. 。私が使用します $set ここに マップ 私たちが達成しようとしていることで。

db.collection.<update method>(
    {},
    [
        {"$set": {"name": { "$concat": ["$firstName", " ", "$lastName"]}}}
    ]
)

mongodb 3.4+

3.4+で使用できます $addFields そしてその $out 集約パイプラインオペレーター。

db.collection.aggregate(
    [
        { "$addFields": { 
            "name": { "$concat": [ "$firstName", " ", "$lastName" ] } 
        }},
        { "$out": "collection" }
    ]
)

これに注意してください コレクションを更新するのではなく、既存のコレクションを交換するか、新しいコレクションを作成します。 また、必要な更新操作についても 「タイプキャスト」クライアントサイド処理が必要になります。 操作に応じて、 find() の代わりに .aggreate() 方法。

MongoDB 3.2および3.0

私たちがこれをする方法は次のとおりです $projectドキュメントを作成して使用します $concat 連結集約文字列を返す文字列集約演算子。私たちはそこから、あなたは反復します カーソル そして、使用します $set オペレーターを更新して、新しいフィールドをドキュメントに追加して使用して追加します バルク操作 効率を最大限に活用してください。

集約クエリ:

var cursor = db.collection.aggregate([ 
    { "$project":  { 
        "name": { "$concat": [ "$firstName", " ", "$lastName" ] } 
    }}
])

mongodb 3.2以下

これから、使用する必要があります bulkWrite 方法。

var requests = [];
cursor.forEach(document => { 
    requests.push( { 
        'updateOne': {
            'filter': { '_id': document._id },
            'update': { '$set': { 'name': document.name } }
        }
    });
    if (requests.length === 500) {
        //Execute per 500 operations and re-init
        db.collection.bulkWrite(requests);
        requests = [];
    }
});

if(requests.length > 0) {
     db.collection.bulkWrite(requests);
}

MongoDB 2.6および3.0

このバージョンからは、現在廃止されたものを使用する必要があります Bulk APIとその 関連する方法.

var bulk = db.collection.initializeUnorderedBulkOp();
var count = 0;

cursor.snapshot().forEach(function(document) { 
    bulk.find({ '_id': document._id }).updateOne( {
        '$set': { 'name': document.name }
    });
    count++;
    if(count%500 === 0) {
        // Excecute per 500 operations and re-init
        bulk.execute();
        bulk = db.collection.initializeUnorderedBulkOp();
    }
})

// clean up queues
if(count > 0) {
    bulk.execute();
}

Mongodb 2.4

cursor["result"].forEach(function(document) {
    db.collection.update(
        { "_id": document._id }, 
        { "$set": { "name": document.name } }
    );
})

他のヒント

繰り返してください。あなたの特定のケースのために:

db.person.find().snapshot().forEach(
    function (elem) {
        db.person.update(
            {
                _id: elem._id
            },
            {
                $set: {
                    name: elem.firstname + ' ' + elem.lastname
                }
            }
        );
    }
);

どうやらmongodb 3.4以来、これを効率的に行う方法があります。 Styvaneの答え.


以下の時代遅れの答え

更新(まだ)でドキュメント自体を参照することはできません。ドキュメントを繰り返して、関数を使用して各ドキュメントを更新する必要があります。見る この答え 例として、または これです サーバー側の場合 eval().

アクティビティが高いデータベースの場合、更新が積極的にレコードの変化に影響する問題に遭遇する可能性があります。このため、使用することをお勧めします snapshot()

db.person.find().snapshot().forEach( function (hombre) {
    hombre.name = hombre.firstName + ' ' + hombre.lastName; 
    db.person.save(hombre); 
});

http://docs.mongodb.org/manual/reference/method/cursor.snapshot/

上記のソリューションを試しましたが、大量のデータに適していないことがわかりました。次に、ストリーム機能を発見しました。

MongoClient.connect("...", function(err, db){
    var c = db.collection('yourCollection');
    var s = c.find({/* your query */}).stream();
    s.on('data', function(doc){
        c.update({_id: doc._id}, {$set: {name : doc.firstName + ' ' + doc.lastName}}, function(err, result) { /* result == true? */} }
    });
    s.on('end', function(){
        // stream can end before all your updates do if you have a lot
    })
})

これについて 答え, 、これによると、スナップショット関数はバージョン3.6で廃止されています アップデート. 。したがって、バージョン3.6以降では、このように操作を実行することが可能です。

db.person.find().forEach(
    function (elem) {
        db.person.update(
            {
                _id: elem._id
            },
            {
                $set: {
                    name: elem.firstname + ' ' + elem.lastname
                }
            }
        );
    }
);

これは、〜150_000レコードのためにあるフィールドを別のフィールドにコピーするために思いついたものです。約6分かかりましたが、同じ数のRubyオブジェクトをインスタンス化して反復するよりも、リソース集中が大幅に少なくなりました。

js_query = %({
  $or : [
    {
      'settings.mobile_notifications' : { $exists : false },
      'settings.mobile_admin_notifications' : { $exists : false }
    }
  ]
})

js_for_each = %(function(user) {
  if (!user.settings.hasOwnProperty('mobile_notifications')) {
    user.settings.mobile_notifications = user.settings.email_notifications;
  }
  if (!user.settings.hasOwnProperty('mobile_admin_notifications')) {
    user.settings.mobile_admin_notifications = user.settings.email_admin_notifications;
  }
  db.users.save(user);
})

js = "db.users.find(#{js_query}).forEach(#{js_for_each});"
Mongoid::Sessions.default.command('$eval' => js)

起動 Mongo 4.2, db.collection.update() 集約パイプラインを受け入れることができ、最終的に別のフィールドに基づいてフィールドの更新/作成を許可します。

// { firstName: "Hello", lastName: "World" }
db.collection.update(
  {},
  [{ $set: { name: { $concat: [ "$firstName", " ", "$lastName" ] } } }],
  { multi: true }
)
// { "firstName" : "Hello", "lastName" : "World", "name" : "Hello World" }
  • 最初の部分 {} 更新するドキュメントをフィルタリングするマッチクエリです(この場合、すべてのドキュメントで)。

  • 2番目の部分 [{ $set: { name: { ... } }] 更新集約パイプラインです(集約パイプラインの使用を示す四角いブラケットに注意してください)。 $set 新しい集約演算子であり、エイリアスです $addFields.

  • 忘れないで { multi: true }, それ以外の場合は、最初の一致するドキュメントのみが更新されます。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top