2010-11-08 2 views
42

У меня есть документ, в MongoDB с 2 уровня глубокого вложенного массива объектов, которые мне нужно обновить, что-то вроде этого:Обновление вложенные массивы в MongoDB

{ 
    id: 1, 
    items: [ 
     { 
      id: 2, 
      blocks: [ 
       { 
        id: 3 
        txt: 'hello' 
       } 
      ] 
     } 
    ] 
} 

Если есть только один глубокий массив уровня я мог бы использовать позиционный оператор обновить объекты в нем, но на второй уровень единственным вариантом я придумал, чтобы использовать позиционный оператор с индексом вложенного объекта, например:

db.objects.update({'items.id': 2}, {'$set': {'items.$.blocks.0.txt': 'hi'}}) 

этого подход работает, но мне кажется опасным для меня, так как я Создание веб-службы и индексный номер должны поступать из кли nt, который может отправить 100000 как индекс, и это заставит mongodb создать массив с 100000 индексами с нулевым значением.

Есть ли какие-либо другие способы обновления таких вложенных объектов, где я могу ссылаться на идентификатор объекта вместо его положения или, возможно, на способы проверки того, был ли предоставленный индекс за пределами, прежде чем использовать его в запросе?

+4

Я предлагаю вам пересмотреть эту схему и найти другой дизайн, чтобы вы могли использовать мощность, предоставляемую MongoDB. Не будет простого/суперэффективного способа обновления определенного элемента в массиве AFAIK. Можете ли вы переконфигурировать это, чтобы вы могли воспользоваться преимуществами addToSet, 'pop' и других операторов массива? – brianz

+0

Спасибо за предложение, да, я могу и на самом деле уже это сделал. Задав этот вопрос, я просто хотел быть уверенным, что ничего не пропустил. – Anton

+2

Я также сталкиваюсь с той же проблемой. Можете ли вы опубликовать образец измененной схемы? – Damodaran

ответ

26

Вот большой вопрос: вам нужно использовать операции «addToSet» и «push» Mongo? Если вы действительно планируете изменять только отдельные элементы в массиве, вы должны, вероятно, построить эти массивы как объекты.

Вот как я бы структурировать следующим образом:

{ 
    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 с expressive updates.

db.objects.update({id: 1 }, { $set: { 'items.$[itm].blocks.$[blk].txt': "hi", } }, { multi: false, arrayFilters: [ { 'itm.id': 2 }, { 'blk.id': 3} ] })

+0

как бы он работал, элементы - это массив. – kapad

+0

Если 'items' - это массив, команда' $ set' не работает. Вам нужно будет структурировать данные по-разному и использовать '$ addToSet'. –

+1

Я искал решение, используя '' '. Что-то вроде 'items. $', Который будет перебирать все элементы массива элементов и вносить изменения, которые я хочу. Любые идеи, что синтаксис будет для чего-то вроде этого. Можно ли это сделать? – kapad

3

Идентификаторы, которые вы используете линейны номер, и он должен прийти откуда-то, как дополнительное поле, например «max_idx» или что-то подобное. Это означает один поиск идентификатора, а затем обновление. UUID/ObjectId можно использовать для идентификаторов, которые гарантируют, что вы также можете использовать Distributed CRUD.

Смежные вопросы