Обновить поле MongoDB, используя значение другого поля
в MongoDB можно ли обновить значение поля, используя значение из другого поля? Эквивалентный SQL будет что-то вроде:
UPDATE Person SET Name = FirstName + ' ' + LastName
и MongoDB в псевдо-код будет такой:
db.person.update( {}, { $set : { name : firstName + ' ' + lastName } );
6 ответов:
вы должны перебрать. Для вашего конкретного случая:
db.person.find().snapshot().forEach( function (elem) { db.person.update( { _id: elem._id }, { $set: { name: elem.firstname + ' ' + elem.lastname } } ); } );
лучший способ сделать это-использовать структуру агрегации для вычисления нашего нового поля.
MongoDB 3.4
наиболее эффективное решение находится в MongoDB 3.4 с помощью
$addFields
и$out
операторы агрегации трубопровода.db.collection.aggregate( [ { "$addFields": { "name": { "$concat": [ "$firstName", " ", "$lastName" ] } }}, { "$out": "collection" } ] )
обратите внимание, что это не обновляет коллекцию, а вместо этого заменяет существующую коллекцию или создает новую. также для операций обновления, которые требуют "тип литья" вам понадобится обработка на стороне клиента, и в зависимости от операции, возможно, вам придется использовать
find()
метод вместо.aggreate()
метод.MongoDB 3.2 и 3.0
как мы делаем это с помощью
$project
ing наши документы и использовать$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 } } ); })
для базы данных с высокой активностью вы можете столкнуться с проблемами, когда ваши обновления влияют на активно изменяющиеся записи, и по этой причине я рекомендую использовать 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 }) })
вот что мы придумали для копирования одного поля в другое для ~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)