Upsert Array Элементы, соответствующие критериям в документе MongoDB? - PullRequest
9 голосов
/ 15 января 2012

Согласно Как обновить критерии соответствия элементов массива в документе MongoDB?

Я хочу сохранить элементы массива, поэтому, если они не совпадают, вставьте их, в противном случае обновите.

Я попытался ответить на этот вопрос, и он отлично работает, если элемент массива уже существует. Если элемент не существует, он создает дочерний элемент «$» в поле массива.

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

Widget (collection)
--Name
--Properties (array)
  --Name
  --Value

Мое приложение получает имя виджета и список свойств из вызова WebService. Я хочу перебрать предоставленные Свойства и обновить значение в MongoDB, если Имя уже существует, ИЛИ вставить новое Свойство в массив Свойства, если его нет.

Ответы [ 3 ]

10 голосов
/ 16 января 2012

То, что вам требуется, невозможно при использовании одного обновления без некоторой логики на стороне приложения. Обратите внимание, что upsert как функция не имеет отношения к этой конкретной проблеме, если вы не хотите автоматически создавать новые документы Widget, если с указанным именем не существует ни одного.

Проблема, с которой вы сталкиваетесь, заключается в том, что нет функциональности, которая позволяет вам выполнять два разных обновления в зависимости от наличия элемента массива. У вас есть только два варианта:

  1. Найдите элемент, определите наличие соответствующих свойств, скомпилируйте соответствующее обновление с вашими новыми или измененными свойствами и выполните его. Это связано с важным недостатком, заключающимся в том, что это не безопасный метод параллелизма. Другими словами, если две веб-службы пытаются сделать это одновременно, это может перезаписать изменения друг друга.
  2. Сделать свойства виджета документами верхнего уровня, а не встроенными. Позволяет вам использовать upserts, чтобы делать то, что вы хотите. Очевидным недостатком является то, что это не очень хороший вариант с точки зрения дизайна схемы. Вы не получите автоматически все свойства, если вы получите, например, виджет.
4 голосов
/ 25 января 2014

Вы не можете атомизировать элементы массива.Но если вы можете реструктурировать свой документ, чтобы использовать объект вместо массива, то это возможно атомарно.Используя вашу нотацию, структура будет

Widget (collection)
  --Name
  --Properties
    --Property1
    --Property2
        .
        :

Пример документа будет

{
    name: 'widget-1',
    properties: {
        'property-1': 100,
        'property-2': 200
    }
}

Чтобы добавить элемент с именем 'property-3' со значением 300, вы должны сделать

db.widgets.update(
    { name: 'widget-1' }, 
    { $set: { 'properties.property-3': 300 } }
)

Один недостаток заключается в том, что запрос имен полей (с использованием оператора запроса $exists) требует сканирования.Вы можете обойти это, добавив дополнительное поле массива, в котором хранятся имена свойств.Это поле может быть проиндексировано и запрошено как обычно.Upserts становятся

db.widgets.update(
    { name: 'widget-1' }, 
    { 
        $set: { 'properties.property-3': 300 },
        $addToSet: { propertyNames: 'property-3' }
    }
)

(Имейте в виду, что $addToSet - это O (n).) При удалении свойства его также необходимо извлечь из массива.

db.widgets.update(
    { name: 'widget-1' }, 
    { 
        $unset: { 'properties.property-3': true },
        $pull: { propertyNames: 'property-3' }
    }
)
1 голос
/ 20 февраля 2013

По крайней мере, в php у меня это работает, попробуйте перевести на свой язык.Ваша коллекция должна выглядеть так:

        {Name: x, Properties: {name1:value1, name2:value2, name3:value3}}

        foreach ($Properties as $name => $value)
        {
            $propertiesArray["Properties.$name"] = $value;                
        }   

        $criteria  = array('Name' => $name);
        $operation = array('$set'   => $propertiesArray);
        $options   = array('upsert' => true);

        $collection = $this->_dbh->selectCollection('Widget');
        $collection->update($criteria, $operation, $options);

Она вставляет новые имена, если они не существуют, и обновляет те, которые уже есть

...