Update in forEach on mongodb shell

To get what you want you will need a few things:

t.forEach(function( aRow ) {
    var newFields = [];
    aRow.fields.forEach( function( aField ){
        var newItems = [];
        aField.items.forEach( function( item ){
            var aNewItem = { item: parseInt(item), ref: 0 };
            newItems.push( aNewItem );
        } );
        newFields.push({ _id: aField._id, items: newItems });
    } )
    aTable.update(
        { _id: aRow._id }, 
        { "$set": { "fields": newFields } }
    );
});

So basically you need to "re-construct" your arrays before updating


You can make changes directly in the whole object and then save it. Try the following snippet

db.aTable.find().forEach(function (itemWrapper){
    itemWrapper.fields.forEach(function(field){
        var items = field.items;
        var newItems = [];
        items.forEach(function(item){
          var t = {'item':item,'key':0}
          newItems.push(t);      
        })
        field.items = newItems;
    })
    db.aTable.save(itemWrapper)
})

What I am doing is iterating over all items and making a new array with {item : 1 , key:0} and then setting it back to items array in field object.

This is the output after update :

{
    "_id" : ObjectId("5332a192ece4ce8362c7a553"),
    "title" : "record 1",
    "fields" : [ 
        {
            "_id" : 1,
            "items" : [ 
                {
                    "item" : 1,
                    "key" : 0
                }
            ]
        }, 
        {
            "_id" : 2,
            "items" : [ 
                {
                    "item" : 2,
                    "key" : 0
                }, 
                {
                    "item" : 3,
                    "key" : 0
                }, 
                {
                    "item" : 4,
                    "key" : 0
                }
            ]
        }, 
        {
            "_id" : 3,
            "items" : [ 
                {
                    "item" : 5,
                    "key" : 0
                }
            ]
        }
    ]
}

/* 1 */
{
    "_id" : ObjectId("5332a192ece4ce8362c7a554"),
    "title" : "record 2",
    "fields" : [ 
        {
            "_id" : 4,
            "items" : [ 
                {
                    "item" : 7,
                    "key" : 0
                }, 
                {
                    "item" : 8,
                    "key" : 0
                }, 
                {
                    "item" : 9,
                    "key" : 0
                }, 
                {
                    "item" : 10,
                    "key" : 0
                }
            ]
        }, 
        {
            "_id" : 5,
            "items" : []
        }, 
        {
            "_id" : 6,
            "items" : [ 
                {
                    "item" : 11,
                    "key" : 0
                }, 
                {
                    "item" : 12,
                    "key" : 0
                }
            ]
        }
    ]
}

Starting Mongo 4.2, db.collection.update() can accept an aggregation pipeline, finally allowing the update of a field based on its current value, and thus using a query instead of javascript:

// {
//   title: "record 1",
//   fields: [
//     { _id: 1, items: [1] },
//     { _id: 2, items: [2, 3, 4] },
//     { _id: 3, items: [5] }
//   ]
// }
db.collection.update(
  {},
  [{ $set: {
       fields: { $map: {
         input: "$fields",
         as: "x",
         in: {
           _id: "$$x._id",
           items: { $map: {
             input: "$$x.items",
             as: "y",
             in: { item: "$$y", key: 0 }
           }}
         }
       }}
  }}],
  { multi: true }
)
// {
//   title: "record 1",
//   fields: [
//     { _id: 1, items: [ { item: 1, key: 0 } ] },
//     { _id: 2, items: [ { item: 2, key: 0 }, { item: 3, key: 0 }, { item: 4, key: 0 } ] },
//     { _id: 3, items: [ { item: 5, key: 0 } ] }
//   ]
// }
  • The first part {} is the match query, filtering which documents to update (in this case all documents).

  • The second part [{ $set: { fields: { $map: { ... } } }] is the update aggregation pipeline (note the squared brackets signifying the use of an aggregation pipeline):

    • $set is a new aggregation operator which in this case replaces the field's value.
    • Then (if we go passed the boilerplate) we're simply $mapping the two nested arrays.
    • More specifically, the second map transformation replaces [1, 2] by [{ item: 1, key: 0 }, { item: 2, key: 0 }].
  • Don't forget { multi: true }, otherwise only the first matching document will be updated.