Reputation: 131
I use MongoDB with PHP driver, so for convenience I will write the query with this syntax, I would like to find a more elegant solution that I found today for the following problem.
I have this collection "Story" with nested document:
Collection Story:
{
"_id":"Story1",
"title":null,
"slug":null,
"sections":[
{
"id_section":"S1",
"index":0,
"type":"0",
"elements":[
{
"id_element":"001",
"text":"img",
"layout":1
}
]
},
{
"id_section":"S2",
"index":0,
"type":"0",
"elements":[
{
"id_element":"001",
"text":"hello world",
"layout":1
},
{
"id_element":"002",
"text":"default text",
"layout":1
},
{
"id_element":"003",
"text":"hello world 3",
"layout":"2"
}
]
}
]
}
Assuming you want to change the value of the element with id_element => 002 present in section with id_section => S2 of Story with _id => Story1
The solution I've found now is to find the "position" of element 002 and do the following
1]
$r=$m->db->plot->findOne(array("_id" => 'Story1',
"sections.id_section"=>'S2'),
array('_id'=>false,'sections.$.elements'=>true));
2]
foreach($r['sections'][0]['elements'] as $key=>$value){
if($value['id_element']=='002'){
$position=$key;
break;
}
3]
$m->db->story->update(array('_id'=>'Story1','sections.id_section'=>'S2','sections.elements.id_element'=>'002'),
array('$set'=>array('sections.$.elements.'.$position.'.text'=>'NEW TEXT')),
array('w'=>1));
I repeat that I do not think an elegant solution, and I noticed that it is a common problem.
Thank you for your help S.
Upvotes: 1
Views: 699
Reputation: 11671
You can't use $ to match multiple levels of nested arrays. This is why it's not a good idea to nest arrays in MongoDB if you anticipate searching on properties anywhere deeper than the top level array. The alternatives for a fixed document structure are to know which positions in all but one of the arrays you want to update at (or to retrieve the document and find out the indexes, as you are doing) or to retrieve the document, update it in the client, and reinsert it.
The other option is to rethink how the data is modeled as documents in MongoDB so that nested arrays don't happen/ In your case, a story is a collection of sections which are collections of elements. Instead of making a story document, you could have a story be represented by multiple section documents. The section documents would share some common field value to indicate they belong to the same story. The above update would then be possible as an update on one section document using the $ positional operator to match and update the correct element.
Upvotes: 1