1

I'm trying write a mongo script to run in RoboMongo that will loop through all documents in a collection. Each document contains an array myArray. The documents look like this:

{
  "name": "myApp",
  "myArray": [
    { "env": "dev", "dbHost": "db2dev.local" },
    { "env": "prod",  "dbHost": "db1prod.local" }
  ]

I want to copy the dbHost field that is defined in dev to prod. So the above result would be:

{
  "name": "myApp",
  "myArray": [
    { "env": "dev", "dbHost": "db2dev.local" },
    { "env": "prod",  "dbHost": "db2dev.local" }
  ]

When I try to access the field myArray[0] I get a syntax error that says:

TypeError: myDoc.myArray[0] is undefined

The function is something like this:

db.myCollection.find().forEach( function(myDoc) {
    var devIdx = 0;
    var prodIdx = 1;
    if (myDoc.myArray[0].env !== 'dev')}
        devIdx = 1;
        prodIdx = 0;
    }
    myDoc.myArray[prodIdx].dbHost = myDoc.myArray[devIdx].dbHost;

    print(myDoc);
});

I've examined the collection (it is very small) and each document has a myArray field as it should with exactly two values (one for dev and one for prod) in the array.

What am I doing wrong? What is the correct syntax to use inside a mongo script? Is updating arrays in a document not supported?

Searching for solution

I've searched and found forEach examples but most are trivial and none include an array being accessed or changed.

The mongo docs are also very simplistic: https://docs.mongodb.com/v3.6/reference/method/cursor.forEach/

PatS
  • 8,833
  • 12
  • 57
  • 100
  • Can you show us what are you getting in myDoc? – Sagar Chaudhary Sep 21 '19 at 14:55
  • The script doesn't run so I just get a syntax error. I'm tracking it down and found another solution on SO that is helping me. I'm learning the Mongo doesn't support accessing the items in an array like normal javascript does. I'll post an answer when I figure it out. If I just have a `forEach(function(myDoc) { print(myDoc) });` then I'll get the input document. – PatS Sep 21 '19 at 15:32
  • Hey I actually did a for loop on an array in my document and modified the object in it. I don't think so that mongo doesn't allow it. – Sagar Chaudhary Sep 21 '19 at 15:40
  • Were you able to get the non-loop working `myArray[0]` as in my question? Just curious. I've re-written using a loop and gotten it working and I'm now working on how to update the document during the loop. – PatS Sep 21 '19 at 15:52
  • I didn't see the non-loop behaviour. But I think it works in a for loop ,then it should work in a non-loop behaviour also. I would like you to try to access the array like this myDoc['myArray'][0] – Sagar Chaudhary Sep 21 '19 at 15:57
  • Thanks for trying, but that didn't work (for me). I have the loop solution working and will post it. – PatS Sep 21 '19 at 16:11

1 Answers1

0

Mongo javascript does not allow you to access arrays directly like you are trying to do (unless you are in a for loop). So a solution is shown below:

db.myCollection.find({}).forEach( function(myDoc) {
    var foundDevEntry = null;
    var updatedProdEntry = false;

    // First time loop to get a copy of the dev entry
    for (var idx in myDoc.myArray) {
        if (myDoc.myArray[idx].env === 'dev') {
            foundDevEntry = myDoc.myArray[idx];
        }
    }

    // 2nd time loop to update the value
    for (var idx in myDoc.myArray) {
        if (myDoc.myArray[idx].env === 'prod') {
            myDoc.myArray[idx].dbHost = foundDevEntry.dbHost;
        }
    }

    // Now update the database with this change
    db.myCollection.update({_id: myDoc._id}, {$set: {"myArray": myDoc.myArray}});

    print(myDoc);   // So results are also returned when query is run
});

I've stripped out error checking to focus on the change required. What (to me) is odd is that the syntax myDoc.myArray[idx] is actually valid but only inside a loop!

The following references helped me come to a solution:

I should add that some solutions I read said that to update an array you had to re-build the array (https://stackoverflow.com/a/22657219/3281336). I did not do that in my solution and it did work but wanted to share it.

PatS
  • 8,833
  • 12
  • 57
  • 100