0

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 }
2
  • 1
    Could you explain better about whant to do ? Because I don't undestand why you are talking about array. I see only list of document. Thanks Commented Aug 4, 2014 at 18:59
  • What I need is to bring all the fruits under a common Key Fruits as a sub document Commented Aug 5, 2014 at 9:00

1 Answer 1

1

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.

Sign up to request clarification or add additional context in comments.

7 Comments

Hi Neil,Thanks I am just learning Mongodb. You are right it is a sub document.I need to group all kinds of fruits in to a key Fruit.Is there any short cut or any staright forward CRUD operation to update mutltiple docs without any other codes. Appreciate your time. Thanks
Neil's answer points out that there is not a way (at present, 2.6) to refer to the existing value of fields in a document for an update, with some exceptions for particular operations like $inc. You'll need to do something along the lines of what Neil suggests.
@pjsuccess As the answer explains MongoDB is presently not capable of using the existing value of one field to update another field in an atomic update. Such as set A=B. The approach as shown requires retrieving the document information in order to issue a separate update. This is essentially what you have to do.
@wdberkeley You have to address other commenters specifically if you want them to get your message. Otherwise the only person who sees comments is the poster of the question or answer you are leaving a comment on.
Thanks Neil,You guys rock.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.