MongoDB: $ addToSet / $ push-документ, только если он еще не существует - PullRequest
9 голосов
/ 05 декабря 2010

У меня есть коллекция lists, где каждый документ имеет массив members. Каждый элемент массива members - это документ со свойством email, свойством creation date и некоторыми другими мета. У меня есть уникальный индекс на members.email, чтобы один и тот же адрес электронной почты не вводился дважды в один и тот же список, но я бы хотел сохранить исходное значение date. К сожалению, ни $addToSet, ни $push, похоже, этого не делают.

Пример использования $ push:

$lists->update(array('_id' => $list['_id'], 'members.email' => array('$ne' => $email)), array('$push' => array('members' => array(
    'email' => $email,
    'date' => new MongoDate(),
    // etc.
))));

И с $ addToSet:

$lists->update(array('_id' => $list['_id']), array('$addToSet' => array('members' => array(
    'email' => $email,
    'date' => new MongoDate(),
    // etc.
))));

Оба примера заменяют весь внедренный документ новым из-за (я полагаю) уникального значения date. Можно ли только $push «членский» документ, если members.email еще не существует, или мне нужно будет сделать это двумя командами?

В качестве альтернативы, было бы лучше масштабировать, чтобы поместить members в их собственную коллекцию с parent_list -подобным свойством?

Ответы [ 5 ]

5 голосов
/ 16 августа 2011

По моему опыту, причина, по которой вы не можете использовать «$ push» или «$ addToSet», заключается в том, что ваши целевые «члены», вероятно, являются встроенным объектом (или преобразованы в один после его создания). Вы можете использовать только $ push, $ pushAll, $ addToSet для встроенных массивов ...

К сожалению, для нас, разработчиков php, запрос курсора всегда возвращает данные в виде массивов, лучший способ проверить, является ли что-то вроде «members» массивом или объектом, - это запустить find () в оболочке mongo и посмотреть для фигурных / квадратных скобок ("{}" или "[]") в результате.

Надеюсь, это поможет.

1 голос
/ 26 июня 2014

используйте $ne в запросе для фильтрации документов с членами одного и того же электронного письма:

$lists->update(array('_id' => $list['_id'],
                     'members.email' => array('$ne' => $email)), 
               array('$addToSet' => array('members' => array(
                     'email' => $email,
                     'date' => new MongoDate(),
                      // etc.
))));
1 голос
/ 05 декабря 2010

Почему бы не иметь коллекцию, в которой вы храните list_id, email, add_date Затем вы создадите уникальный индекс для 2 ключей list_id и email. Это предотвратит вставку записи с тем же list_id и адресом электронной почты

Там не будет встроенных документов. Вы все еще можете использовать find () по list_id

0 голосов
/ 06 января 2012

IIUC, попробуйте использовать Upsert , чтобы либо обновить встроенный документ (если он есть), либо вставить его (если его нет).См. Также this .

Что касается вас, второй вопрос, касающийся масштабируемости, он действительно зависит от регистра ... встраивание документа в родительский элемент требует более сложных обновлений и большего извлечения данных, но сокращает количество запросов выборкисосчитать.

0 голосов
/ 05 декабря 2010

Да, это возможно. Ознакомьтесь с оператором $ существующие в разделе mongoDB расширенные запросы . Запишите условие $ существует в оператор where, который будет обновляться только тогда, когда электронная почта участников не существует.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...