Операторы обновления полей

Набор модификаторов, который меняет поля, не вытягивая документ в приложение.

Операторы обновления позволяют менять отдельные поля атомарно прямо в базе — без чтения документа и обратной записи.

$set и $unset

С $set вы уже знакомы — он задаёт значение поля (создаёт, если поля не было). Противоположность — $unset, который удаляет поле из документа целиком:

db.users.updateOne(
  { name: "Анна" },
  { $unset: { phone: "" } }
)

Значение справа от $unset не важно (принято писать "") — поле просто исчезнет. Это не то же, что выставить null: при $unset поля не будет вообще, и $exists: false станет для него истинным.

$inc — увеличить или уменьшить число

Менять числовое поле через чтение-и-запись опасно: между ними кто-то может изменить значение. $inc делает это атомарно. Уменьшим остаток на складе на 1 при продаже:

db.products.updateOne(
  { _id: "SKU-1001" },
  { $inc: { stock: -1 } }
)

Положительное число увеличивает, отрицательное уменьшает. Удобно для счётчиков просмотров, лайков, остатков:

db.articles.updateOne(
  { _id: "a1" },
  { $inc: { views: 1 } }
)

$mul, $min, $max

Похожие числовые операторы: $mul умножает поле, $min/$max записывают значение только если оно меньше/больше текущего. Поднять цену на 10%:

db.products.updateMany(
  {},
  { $mul: { price: 1.1 } }
)

Зафиксировать рекордный максимум, не перезаписывая большее значение меньшим:

db.players.updateOne(
  { _id: "p1" },
  { $max: { highScore: 980 } }
)

$rename — переименовать поле

Когда нужно сменить имя поля во всех документах (например, привести базу к новому соглашению), пригодится $rename:

db.users.updateMany(
  {},
  { $rename: { "fio": "fullName" } }
)

Почему это лучше, чем читать и переписывать

Можно было бы прочитать документ в приложение, поменять число и записать обратно. Но это две операции с «окном» между ними: параллельный запрос успеет вмешаться, и счётчик собьётся. Операторы вроде $inc выполняются на сервере атомарно — для одного документа MongoDB гарантирует, что обновление не перемешается с другими. Это и быстрее, и безопаснее.

Итог

  • $set задаёт поле, $unset удаляет его (не путать с установкой null).
  • $inc атомарно меняет число — идеален для счётчиков и остатков; есть также $mul, $min, $max.
  • Операторы работают на сервере атомарно для одного документа — безопаснее, чем «прочитал-изменил-записал».
Проверьте себя
1. Что делает оператор $unset?
AУстанавливает поле в null
BПолностью удаляет поле из документа
CОбнуляет числовое поле
DПереименовывает поле
2. Какой оператор атомарно увеличивает числовое поле?
A$set
B$inc
C$push
D$rename
3. Почему $inc предпочтительнее, чем прочитать значение, увеличить и записать обратно?
AОн короче по символам
BОн выполняется на сервере атомарно, без гонки между чтением и записью
CОн работает быстрее только в Atlas
DМежду ними нет разницы
Поддержать проект