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

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

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

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

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

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

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

Результат:

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

$set обязателен: иначе вы перезапишете документ

Это ловушка для новичков. Если передать голый объект без оператора, MongoDB не «дополнит» поля, а заменит документ целиком. Такой вызов сотрёт все остальные поля товара, оставив один price:

// ОПАСНО: это replace, а не update
db.products.updateOne(
  { name: "Кофе" },
  { price: 500 }
)

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

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 — все подходящие; фильтр {} — это «все».
Проверьте себя
1. Что произойдёт при вызове updateOne с обновлением { price: 500 } без $set?
AИзменится только поле price
BДокумент будет заменён целиком — остальные поля исчезнут
CВозникнет ошибка синтаксиса
DДобавится новый документ
2. Чем updateMany отличается от updateOne?
AupdateMany работает только с числами
BupdateMany обновляет все документы, подходящие под фильтр, а не только первый
CupdateMany не требует фильтра
DРазличий нет
3. Почему matchedCount может быть больше modifiedCount?
AЭто всегда ошибка
BДокументы нашлись, но новое значение совпало со старым, поэтому изменения не было
CТак работает только updateOne
DmodifiedCount считает удалённые документы
Поддержать проект