Я использую MongoDB с PHP-драйвером, поэтому для удобства я напишу запрос с этим синтаксисом, я хотел бы найти более элегантное решение, которое я нашел сегодня для следующей проблемы.
У меня есть эта коллекция "История" с вложенным документом:
История коллекции:
{
"_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"
}
]
}
]
}
Предполагая, что вы хотите изменить значение элемента с id_element => 002, присутствующим в разделе с id_section => S2 Story с помощью _id => Story1
Решение, которое я нашел сейчас, это найти "позицию" элемента 002 и сделать следующее
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));
Повторяю, я не думаю, что это элегантное решение, и я заметил, что это обычная проблема.
Спасибо за помощь.
Вы не можете использовать $ для соответствия нескольких уровней вложенных массивов. Вот почему не рекомендуется встраивать массивы в MongoDB, если вы ожидаете поиска по свойствам где-нибудь глубже, чем массив верхнего уровня. Альтернативы для фиксированной структуры документа должны знать, какие позиции во всех, кроме одного из массивов, которые вы хотите обновить (или получить документ и узнать индексы, как вы это делаете) или получить документ, обновить его в клиент, и повторно вставьте его.
Другой вариант - переосмыслить, как данные моделируются как документы в MongoDB, так что вложенные массивы не происходят/В вашем случае история представляет собой набор разделов, который представляет собой набор элементов. Вместо создания документа истории вы могли бы представить историю несколькими документами нескольких разделов. В документах разделов будет указано общее значение поля, чтобы указать, что они принадлежат к одной и той же истории. Приведенное выше обновление было бы возможно в качестве обновления одного документа раздела с использованием оператора $ positional для сопоставления и обновления правильного элемента.