简体   繁体   中英

Update multiple documents / add keys and its values in to a new array

I have collection named inventory where I have multiple documents that has values for each doc

{ "apples": 2 ,"oranges": 3,  "carrots": 5 }
{ "apples": 4, "oranges": 6, "carrots": 9 }

How do I update push all fruits in to a single array on multiple documents like so:

{ "fruits": { "apples":2 ,"oranges":3 }, "carrots": 5 }

First thing to note here is that the example you give is not an array but just a sub-document for "fruits" that has different keys. An "array" in MongoDB would look like this:

{ "fruits": [{ "apples":2 } , { "orange":3 }], "carrot": 5 }

Also, aside from the term "fruits" being subjective, as with no other identifier you would have to specify a "list" of things that qualify as fruits, the other thing to consider is that there is no actual way in MongoDB at present to refer to the existing value of a field when processing an update.

What that means is you need to .find() each document to retrieve the data in order to be able to work with the sort of "re-structure" that you want. This essentially means looping the results an performing an .update() operation for each document.

The Bulk API for MongoDB 2.6 and greater can be of some help here, where at least the "write" operations to the database can be sent in batches, rather than one at a time:

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

var fruits = ["apples","oranges"];
var unset = {};

fruits.forEach(function(fruit) {
   unset[fruit] = 1;
});


db.collection.find({}).forEach(function(doc) {

   var fields = [];
   fruits.forEach(function(fruit) {
       if ( doc.hasOwnProperty(fruit) ) {
           var subDoc = {};
           subDoc[fruit] = doc[fruit];
           fields.push(subDoc);
       }
   });

   bulk.find({ "_id": doc._id }).updateOne({ 
       "$unset": unset, "$push": { "fruits": { "$each": fields } }
   });
   count++;

   if ( count % 1000 == 0 ) {
       bulk.execute();
       var bulk = db.collection.initializeOrderedBulkOp();
   }

});

if ( count % 1000 != 0 )
    bulk.execute();

That also uses the $each modifier for $push in order to add multiple array entries at once. The $unset operator can be safely called for fields that don't exist in the document so there is no need to check for their presence in the document as is otherwise required when constructing the array of elements to $push .

Of course if you actually want a document like what you gave an example of that is not actually an array, then you construct differently with the $set operator:

   var fields = {};
   fruits.forEach(function(fruit) {
       if ( doc.hasOwnProperty(fruit) )
           fields[fruit] = doc[fruit];
   });

   bulk.find({ "_id": doc._id }).updateOne({ 
       "$unset": unset, "$set": { "fruits": fields }
   });

Whatever the case is you need to loop the existing collection. There is no operation that allows you to "take" an existing value in a document and "use it" in order to set a new value from a server side perspective.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM