В первой главе мы рассмотрели создание, чтение и удаление документов. Для обновления важно различать три операции: replaceOne заменяет документ, updateOne изменяет один подходящий документ, а updateMany — все подходящие документы. Здесь они разбираются на одних и тех же учебных данных.
Обновление данных: замена и $set
Для полной замены документа используйте replaceOne: первый аргумент — фильтр, второй — новый документ без операторов обновления. Следующая команда намеренно убирает прежние поля, кроме неизменяемого _id; это демонстрация замены, а не безопасный способ поменять вес.
db.unicorns.replaceOne({name: 'Roooooodles'}, {weight: 590})
Если учебные данные уже менялись, сначала проверьте, что выбрана база learn, и восстановите набор из главы 1. Не очищайте коллекции в рабочей базе ради повторения примера.
В реальной жизни, конечно, следует обновлять документы, выбирая их по _id, однако, поскольку я не знаю какой _id MongoDB сгенерировала для вас, будем выбирать по имени — name. Теперь, давайте взглянем на обновленную запись:
db.unicorns.find({name: 'Roooooodles'})
Документ не находится по имени, потому что replaceOne заменил его полями нового документа и сохранил _id. В отличие от замены, updateOne с $set меняет только названные поля. Разные методы делают намерение явным и уменьшают риск случайной потери остальных полей.
db.unicorns.updateOne({weight: 590}, {$set: {name: 'Roooooodles', dob: new Date(1979, 7, 18, 18, 44), loves: ['apple'], gender: 'm', vampires: 99}})
Это восстановит утерянные ранее поля. Поле weight не перезапишется, поскольку мы его не передали в запрос. Теперь, если выполнить:
db.unicorns.find({name: 'Roooooodles'})
мы получим ожидаемый результат. Таким образом, в первом примере правильно было бы обновить weight следующим образом:
db.unicorns.updateOne({name: 'Roooooodles'}, {$set: {weight: 590}})
Модификаторы обновления
Кроме $set можно использовать и другие модификаторы для разных изящных вещей. Все эти модификаторы обновления действуют над полями — так что ваш документ не окажется перезаписан целиком. Например, модификатор $inc служит для того, чтобы изменить поле на положительную (увеличить) или отрицательную (уменьшить) величину. Например, если единорог Pilot был ошибочно награждён за убийство пары лишних вампиров, мы можем исправить эту ошибку следующим образом:
db.unicorns.updateOne({name: 'Pilot'}, {$inc: {vampires: -2}})
Если Aurora внезапно пристрастилась к сладостям, мы можем добавить соответствующее значение к ее полю loves с помощью модификатора $push:
db.unicorns.updateOne({name: 'Aurora'}, {$push: {loves: 'sugar'}})
Операторы $set, $inc и $push изменяют соответствующие поля. Обычное обновление не заменяет весь документ; для полной замены существует replaceOne.
Обновление/вставка
Опция {upsert: true} у updateOne создаёт документ, если фильтр не нашёл совпадения. При наличии совпадения выполняется обновление. Указывать следует объект опций, а не исторические позиционные булевы параметры.
В учебной коллекции hits будем считать посещения страницы. Одна операция с $inc и upsert объединяет создание и увеличение счётчика. Для конкурентных записей полезен уникальный индекс по page: без ограничения уникальности модель не гарантирует один документ на страницу.
db.hits.updateOne({page: 'unicorns'}, {$inc: {hits: 1}});
db.hits.find();
Сначала задайте уникальность страницы: db.hits.createIndex({page: 1}, {unique: true}). Затем вызовите обновление с опцией upsert:
db.hits.updateOne({page: 'unicorns'}, {$inc: {hits: 1}}, {upsert: true});
db.hits.find();
Поскольку документы с полем page, равным unicorns, не существуют, то будет создан новый документ. Если выполнить это вторично, существующий документ будет обновлён, и поле hits увеличится до 2.
db.hits.updateOne({page: 'unicorns'}, {$inc: {hits: 1}}, {upsert: true});
db.hits.find();
Множественные обновления
updateOne обновляет один подходящий документ. Когда задача относится ко всей выборке, выбирайте updateMany. Например, следующий вызов намеренно обновляет лишь одного учебного единорога:
db.unicorns.updateOne({}, {$set: {vaccinated: true }});
db.unicorns.find({vaccinated: true});
Чтобы изменить все документы, задайте updateMany; пустой фильтр охватывает всю учебную коллекцию:
db.unicorns.updateMany({}, {$set: {vaccinated: true }});
db.unicorns.find({vaccinated: true});
В этой главе
Для полной замены используйте replaceOne, для изменения одного документа — updateOne, для всей выборки — updateMany. $set сохраняет неназванные поля, $inc изменяет число, $push дополняет массив, а {upsert: true} разрешает создание при отсутствии совпадения. Перед записью проверьте фильтр чтением.
Примеры рассчитаны на mongosh. В драйвере приложения используйте его собственную документацию и проверяйте результат операции: число найденных и изменённых документов, идентификатор вставки при upsert и ошибки. Старые позиционные параметры upsert/multi в эту редакцию не переносятся.