Обновление: updateOne и updateMany

Как изменить существующие документы и почему почти всегда нужен оператор $set.

updateOne меняет первый подходящий документ, updateMany — все подходящие; оба принимают фильтр и описание изменений.

Структура команды обновления

У методов обновления два аргумента: фильтр (какие документы менять, синтаксис тот же, что в find) и обновление (что именно изменить, с операторами-модификаторами). Поднимем цену одному товару:

db.products.updateOne(
  { name: "Кофе" },
  { $set: { price: 500 } }
)

В ответе видно, сколько документов нашлось и сколько реально изменилось:

Результат:

{
  "acknowledged": true,
  "matchedCount": 1,
  "modifiedCount": 1
}

$set обязателен: иначе будет ошибка

Это ловушка для новичков. Если передать голый объект без оператора, updateOne/updateMany не «дополнят» поля и не выполнят обновление, а выбросят ошибку. Такой вызов завершится с MongoServerError: Update document requires atomic operators:

// ОШИБКА: updateOne требует оператор-модификатор
db.products.updateOne(
  { name: "Кофе" },
  { price: 500 }
)

Правильно — заворачивать изменения в $set: он меняет указанные поля, а остальные не трогает. Несуществующее поле $set добавит. (Если нужна именно полная замена документа — для этого есть отдельный метод replaceOne.)

db.products.updateOne(
  { name: "Кофе" },
  { $set: { price: 500, discount: 10 } }
)

updateMany — массовое обновление

updateOne остановится на первом совпадении. Чтобы обновить все подходящие документы, нужен updateMany. Пометим все товары из Казани как доступные:

db.products.updateMany(
  { city: "Казань" },
  { $set: { inStock: true } }
)

Поднять цену сразу на весь каталог? Фильтр {} совпадает со всеми документами:

db.products.updateMany(
  {},
  { $set: { currency: "RUB" } }
)

matched и modified — не одно и то же

В ответе два счётчика: matchedCount — сколько документов подошло под фильтр, modifiedCount — сколько реально изменилось. Если новое значение совпадает со старым, документ найден, но не изменён — тогда matched больше modified. Это нормально и помогает понять, что произошло.

Итог

  • У обновления два аргумента: фильтр и описание изменений с операторами.
  • Без оператора ($set и др.) updateOne/updateMany выбрасывают ошибку (Update document requires atomic operators) — частая ошибка новичков.
  • updateOne меняет один документ, updateMany — все подходящие; фильтр {} — это «все».
Проверьте себя
1. Что произойдёт при вызове updateOne с обновлением { price: 500 } без $set?
AИзменится только поле price
BДокумент будет заменён целиком — остальные поля исчезнут
CДобавится новый документ
DВозникнет ошибка: updateOne требует оператор-модификатор
2. Чем updateMany отличается от updateOne?
AupdateMany обновляет все документы, подходящие под фильтр, а не только первый
BupdateMany работает только с числами
CupdateMany не требует фильтра
DРазличий нет
3. Почему matchedCount может быть больше modifiedCount?
AЭто всегда ошибка
BДокументы нашлись, но новое значение совпало со старым, поэтому изменения не было
CТак работает только updateOne
DmodifiedCount считает удалённые документы