1

I have some data stored in my MongoDB. The "tags" fields is actually an array of Strings, but we have to update our model to store more data with those tags

Current model document

{
    "id" : "59e4aefd74f12800019ba565",
    "title" : "This is a title",
    "tags" : [ 
        "59b02e6f6b28ce0001f8c0a8", 
        "59b031886b28ce0001f8c0af", 
        "59ba8c1a5047570001a3c078"
    ]
}

Desired model after update

{
    "id" : "59e4aefd74f12800019ba565",
    "title" : "This is a title",
    "tags" : [ 
        {
            "id" : "5a82ff1d889a15000103b115",
            "internalName" : "Día Mundial de la Television"
        }, 
        {
            "id" : "59ba8c1a5047570001a3c078",
            "internalName" : "menu"
        }, 
        {
            "id" : "5a26ac73d0fc2e00017f286e",
            "internalName" : "oid_asdf_asd"
        }
    ],


}

Now tags is a embedded object (forget about internalName field). How can I update the tag field without losing those data? I've tried with $rename, but it doesn't work well with arrays

db.test.update({}, {$rename: {'tags': 'tags2.id'}})
Ricardo Pontual
  • 3,749
  • 3
  • 28
  • 43
dragonalvaro
  • 703
  • 1
  • 10
  • 28

1 Answers1

1

Using the concepts from this very good answer, you could create a cursor from an aggregate operation that transforms the tags arrays by using the $map operator, iterate the cursor and update your collection using bulkWrite. The aggregate operation follows:

var cursor = db.test.aggregate([
    {
        "$project": {
            "tags": {
                "$map": {
                    "input": "$tags",
                    "as": "el",
                    "in": { 
                        "id": "$$el",
                        "internalName": { "$literal": "temp string" }
                    }
                }
            }
        }
    }
]);

And running the bulk update:

var bulkUpdateOps = [];

cursor.forEach(doc => {
    const { _id, tags } = doc;
    bulkUpdateOps.push({
        "updateOne": {
           "filter": { _id },
           "update": { "$set": { tags } },
           "upsert": true
        }
    });

    if (bulkUpdateOps.length === 1000) {
        db.test.bulkWrite(bulkUpdateOps);  
        bulkUpdateOps = [];                 
    }
}); 

if (bulkUpdateOps.length > 0) {
    db.test.bulkWrite(bulkUpdateOps);
}
chridam
  • 100,957
  • 23
  • 236
  • 235
  • thanks for the reply. I'm testing it right now but it doesn't seems to works. (My bad probably). I'm trying to figure out why. – dragonalvaro Feb 19 '18 at 16:46
  • @dragonalvaro There was a typo in the code which I removed, try again – chridam Feb 19 '18 at 18:04
  • 1
    Now it worked! Thank you very much. I really appreciate your time. – dragonalvaro Feb 20 '18 at 09:05
  • Just one more correction, move the first if condition check into the forEach loop for it to work correctly i.e. execute the bulk updfate in batches of 1000. I have updated my answer to reflect this. – chridam Feb 20 '18 at 09:08