使用另一个字段的值更新MongoDB字段

在MongoDB中,是否可以使用来自其他字段的值来更新字段的值? 等价的SQL将是这样的:

UPDATE Person SET Name = FirstName + ' ' + LastName 

而MongoDB的伪代码是:

 db.person.update( {}, { $set : { name : firstName + ' ' + lastName } ); 

您不能在更新(还)中引用文档本身。 您需要遍历文档并使用函数更新每个文档。 看到这个答案的例子,或者这个服务器端eval()

你应该迭代。 对于您的具体情况:

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

最好的方法是使用聚合框架来计算我们的新领域。

MongoDB 3.4

最有效的解决scheme是在MongoDB 3.4中使用$addFields$out聚合pipe道运算符。

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

请注意,这不会更新您的collections集,而是replace现有的collections集或创build一个新的collections集。 此外,对于需要“types转换”的更新操作, 您将需要客户端处理,根据操作,您可能需要使用find()方法,而不是.aggreate()方法。

MongoDB 3.2和3.0

我们这样做的方式是$project我们的文档,并使用$concatstring聚合运算符返回串联的string。 我们从那里,然后迭代游标并使用$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 } } ); }) 

对于具有较高活动的数据库,您可能遇到更新影响主动更改logging的问题,因此,我build议使用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/

我尝试了上述解决scheme,但我发现它不适合大量的数据。 然后我发现了streamfunction:

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

以下是我们想到的将一个字段复制到另一个字段的约150_000个logging。 这花了大约6分钟的时间,但是比实例化和遍历相同数量的ruby对象所需要的资源要less得多。

 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)