Раздел 36 · MongoDB с нуля

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

junior~20 мин

без входаВойди, чтобы отмечать прогресс, вести заметки и решать задачи в редакторе. войти

В прошлом уроке мы собирали витрину: находили книги, сортировали и резали на страницы. Но магазин живёт: цены меняются, книги заканчиваются, у отзывов появляются ответы. Сегодня учимся менять документы, которые уже лежат в базе. Примеры на учебной базе магазина.

Исправление в карточке

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

Обновление в MongoDB устроено так же, и у него две части:

  1. Фильтр: какие документы менять. Это тот же образец, что мы передавали в find.
  2. Документ обновления: что в них поменять. Он состоит из операторов, самый частый из которых $set, «поставь полю такое значение».

Если сравнивать с SQL, фильтр это WHERE, а $set это SET из урока про UPDATE в PostgreSQL.

updateOne: один документ

«Собачье сердце» подорожало до 420 рублей:

db.books.updateOne({ _id: 2 }, { $set: { price: 420 } })
db.books.findOne({ _id: 2 }, { title: 1, price: 1 })
{
  acknowledged: true,
  insertedId: null,
  matchedCount: 1,
  modifiedCount: 1,
  upsertedCount: 0
}
{ _id: 2, title: 'Собачье сердце', price: 420 }

Сервер отвечает не самим документом, а отчётом о работе. Нам важны два числа:

  • matchedCount: сколько документов подошло под фильтр;
  • modifiedCount: сколько из них действительно изменилось.

Остальные поля пригодятся в уроке про upsert, где обновление умеет превращаться во вставку.

Числа расходятся чаще, чем кажется. Выполним ту же команду ещё раз:

db.books.updateOne({ _id: 2 }, { $set: { price: 420 } })
{
  acknowledged: true,
  insertedId: null,
  matchedCount: 1,
  modifiedCount: 0,
  upsertedCount: 0
}

Документ нашёлся, но цена уже 420, менять нечего. MongoDB не перезаписывает поле тем же значением и честно пишет modifiedCount: 0.

А если фильтр не подошёл ни к одному документу, ошибки не будет. Будут два нуля:

db.books.updateOne({ _id: 42 }, { $set: { price: 100 } })
{
  acknowledged: true,
  insertedId: null,
  matchedCount: 0,
  modifiedCount: 0,
  upsertedCount: 0
}

Поэтому после обновления в коде приложения смотрят на matchedCount. Ноль там значит «такой книги нет», и это стоит показать пользователю, а не молча считать, что всё получилось.

урок из курса

Урок из курса «MongoDB с нуля». Курс покупается один раз и остаётся навсегда: 500 ₽.

Оплата пока не подключена: премиум и курсы открываются промокодом.