Обновление вложенных массивов в mongodb
У меня есть документ в mongodb с 2-уровневым вложенным массивом объектов, который мне нужно обновить, примерно так:
{
id: 1,
items: [
{
id: 2,
blocks: [
{
id: 3
txt: 'hello'
}
]
}
]
}
Если бы существовал только один уровень глубокого массива, я мог бы использовать позиционный оператор для обновления объектов в нем, но для второго уровня единственным вариантом, который я выбрал, является использование позиционного оператора с индексом вложенных объектов, например:
db.objects.update({'items.id': 2}, {'$set': {'items.$.blocks.0.txt': 'hi'}})
Этот подход работает, но мне кажется опасным, поскольку я строю веб-службу, а индексный номер должен поступать от клиента, который может отправить 100000 в качестве индекса, и это заставит mongodb создать массив с 100000 индексами с нулевым значением.
Существуют ли какие-либо другие способы обновления таких вложенных объектов, где я могу ссылаться на идентификатор объекта вместо его положения или, возможно, на способы проверки того, был ли предоставленный индекс за пределами, прежде чем использовать его в запросе?
Ответы
Ответ 1
Вот большой вопрос: нужно ли вам использовать Mongo "addToSet" и "push" операции? Если вы действительно планируете изменять только отдельные элементы в массиве, то вы должны, вероятно, построить эти массивы как объекты.
Вот как бы я это структурировал:
{
id: 1,
items:
{
"2" : { "blocks" : { "3" : { txt : 'hello' } } },
"5" : { "blocks" : { "1" : { txt : 'foo'}, "2" : { txt : 'bar'} } }
}
}
В основном это преобразует все в объекты JSON вместо массивов. Вы теряете возможность использовать $push
и $addToSet
, но я думаю, что это делает все проще. Например, ваш запрос будет выглядеть так:
db.objects.update({'items.2': {$exists:true} }, {'$set': {'items.2.blocks.0.txt': 'hi'}})
Вы также заметите, что я сбросил идентификаторы. Когда вы вложите такие вещи, вы обычно можете заменить "ID" простым использованием этого числа в качестве индекса. Теперь подразумевается концепция "ID".
Эта функция была добавлена в 3.6 с выразительными обновлениями.
db.objects.update( {id: 1 }, { $set: { 'items.$[itm].blocks.$[blk].txt': "hi", } }, { multi: false, arrayFilters: [ { 'itm.id': 2 }, { 'blk.id': 3} ] } )
Ответ 2
Иды, которые вы используете, являются линейным числом, и оно должно происходить откуда-то вроде дополнительного поля, такого как max_idx или что-то подобное.
Это означает один поиск идентификатора, а затем обновление. UUID/ObjectId можно использовать для идентификаторов, которые гарантируют, что вы также можете использовать Distributed CRUD.