Одновременно потяните и добавьте в себя монго

У меня есть коллекция, элементы которой могут быть упрощены до этого:

{tags : [1, 5, 8]}

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

db.colll.update({
  tags : 1
},{
  $pull: { tags: 1 },
  $addToSet: { tags: 2 }
}, {
  multi: true
})

Прохладный, так что он найдет все элементы, у которых есть тег, который мне не нужен (1), удалите его и добавьте еще (2), если его еще нет. Проблема в том, что я получаю сообщение об ошибке:

"Невозможно обновить теги и теги одновременно

В основном это означает, что я не могу делать pull и addtoset одновременно. Есть ли другой способ, которым я могу это сделать?

Конечно, я могу запомнить все идентификаторы элементов, а затем удалить тег и добавить в отдельные запросы, но это не очень приятно.

Ответ 1

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

Вы можете сделать это с помощью "последовательного", как вы можете получить с "объемный" API операций или в другой форме" обновление. В пределах, конечно, а также наоборот:

var bulk = db.coll.initializeOrderedBulkOp();
bulk.find({ "tags": 1 }).updateOne({ "$addToSet": { "tags":  2 } });
bulk.find({ "tags": 1 }).updateOne({ "$pull": { "tags": 1 } });

bulk.execute();

Не гарантирует, что ничто другое не попытается изменить, но оно будет как можно ближе к вам.

Также смотрите исходную команду "update" с несколькими документами.

Ответ 2

Если вы удаляете и добавляете одновременно, вы можете моделировать "карту", ​​а не "набор". Если это так, объект может работать меньше, чем массив.

Вместо данных в виде массива:

{ _id: 'myobjectwithdata',
  data: [{ id: 'data1', important: 'stuff'},
         { id: 'data2', important: 'more'}]
}

Использовать данные как объект:

{ _id: 'myobjectwithdata',
  data: { data1: { important: 'stuff'},
          data2: { important: 'more'} }
}

Теперь одно командное обновление:

db.coll.update(
  'myobjectwithdata', 
  { $set: { 'data.data1': { important: 'treasure' } }
);

Жесткий мозг работает над этим ответом здесь и здесь.