7

MongoDB updating fields in nested array

How can I set "play" to "play photo" in the photos array?

I only know its _id.

"_id": ObjectId("4f41a5c7c32810e404000000"),
"albums": [
{
    "_id": ObjectId("4f545d1bc328103812000000"),
    "name": "album1" ,
    "photos":[{
        "_id": ObjectId("4f545d1bc328103812d00000"),
        "name":"travel photo"
    },{
        "_id": ObjectId("4f545d1bc328103812c00000"),
        "name":"play"
    }]
},
{
    "_id": ObjectId("4f545f56c328103c12000000"),
    "name": "album2" 
},
{
    "_id": ObjectId("4f545f68c328103012000000"),
    "name": "album3" 
},
{
    "_id": ObjectId("4f546642c328103c12000001"),
    "name": "album4" 
}]
turivishal
  • 34,368
  • 7
  • 36
  • 59
tonilin
  • 135
  • 1
  • 1
  • 8

3 Answers3

18

This issue has been resolved. Feature of updating fields inside nested array of objects, is available in MongoDB 3.6+ versions. Look positional operators(all and with identifier) here.

//Update all docs in collection matching photo name "play" to "play photo"
db.collectioname.update(
    {},
    { $set: { "albums.$[].photos.$[photo_field].name": "play photo" } },
    { arrayFilters: [  {"photo_field.name": "play"} ], multi: true}
);

//Update this specific doc given in question matching photo name "play" to "play photo"
db.collectioname.update(
    {"_id" : ObjectId("4f41a5c7c32810e404000000")},
    { $set: { "albums.$[].photos.$[photo_field].name": "play photo" } },
    { arrayFilters: [  {"photo_field.name": "play"} ]}
);

This is for the help of people coming here after MongoDB 3.6

Abdul Moiz
  • 1,317
  • 2
  • 17
  • 40
  • what does `photo_field` in your example mean? Does it mean go through all fields in photo until you find `name`? – Keselme Oct 03 '19 at 12:09
  • Yes, it will go through each index of albums and then photos and match "play" and then sets it to "play photo". – Abdul Moiz Oct 05 '19 at 04:04
7

You can't. The positional operator is only available for the first array in your document hierarchy. As such you cannot manipulate individual elements of deeper nester arrays.

It's a known issue and is scheduled for development here : https://jira.mongodb.org/browse/SERVER-831

Until that time you'll have to normalize your schema a bit I'm afraid.

Remon van Vliet
  • 18,365
  • 3
  • 52
  • 57
  • Thanks, but I may not change my schema. I will pull the element,modify,and push back. although its Inefficient. – tonilin Mar 08 '12 at 09:13
  • 3
    That's an option but has serious concurrency issues (one write can overwrite the results of a concurrent write) so use with care. – Remon van Vliet Mar 08 '12 at 09:25
0

Tonilin You Cant Update this type of array directly for this you need to find the index of that array in which you want to update like if you want to update name as play photo then you have to find the index of photos where name is play. For this I used this code like:

$m = new Mongo();
$db=$m->yourdatabase;
//testarray is my collection name
$result=$db->testarray->find();
$index='';
foreach($result as $res)
{
    if(array_key_exists("albums",$res))
    {
        foreach($res['albums'] as $ralbum)
        {
            if(array_key_exists("photos",$ralbum))
            {
                foreach($ralbum['photos'] as $k=>$rphotos)
                {
                    if(array_key_exists("name",$rphotos))
                        if($rphotos['name']=='play')
                            $index=$k;
                }
            }
        }
    }
}
//echo $index;

// Now to update this value in your database use this code...

if($index!=='')
{
    //Run like this in Shell
    //db.testarray.update({"albums.photos._id":ObjectId("4f545d1bc328103812d00000")},{'$set':{"albums.$.photos.1.name":"play132"}})
    $condition=array("albums.photos._id"=>new MongoId("4f545d1bc328103812d00000"));
    $data=array('$set'=>array("albums.$.photos.".$index.".name"=>"play photo"));
    $result=$db->testarray->update($condition,$data);
    $status=$db->Command(array('getlasterror'=>1));
    print_r($status);
}
Rohan Kumar
  • 40,431
  • 11
  • 76
  • 106