В прошлом уроке операторы меняли отдельные поля: числа, строки, вложенные документы. Но у книг магазина есть и массивы: метки в tags и отзывы в reviews. Положить в массив новый элемент, убрать лишний, поправить оценку в одном отзыве из трёх: для этого у MongoDB свой набор операторов. Примеры идут по порядку на учебной базе магазина.
Список покупок на холодильнике
На холодильнике висит список покупок. С ним делают несколько разных вещей: дописывают строчку внизу, вычёркивают купленное, дописывают «молоко», только если его ещё нет в списке, исправляют «2 батона» на «3 батона». Переписывать весь список ради одной строчки никто не станет.
С массивами в документе так же. В SQL метки книги лежали бы в отдельной таблице, и каждую правку мы делали бы INSERT или DELETE в ней. В MongoDB массив живёт внутри документа, и его правят прямо там.
$push: дописать в конец
$push добавляет элемент в конец массива. Отметим «Собачье сердце» как бестселлер. Для наглядности выполним команду дважды:
db.books.updateOne({ _id: 2 }, { $push: { tags: 'бестселлер' } })
db.books.updateOne({ _id: 2 }, { $push: { tags: 'бестселлер' } })
db.books.findOne({ _id: 2 }, { title: 1, tags: 1 })
{
acknowledged: true,
insertedId: null,
matchedCount: 1,
modifiedCount: 1,
upsertedCount: 0
}
{
acknowledged: true,
insertedId: null,
matchedCount: 1,
modifiedCount: 1,
upsertedCount: 0
}
{
_id: 2,
title: 'Собачье сердце',
tags: [ 'классика', 'сатира', 'бестселлер', 'бестселлер' ]
}
$push не проверяет, есть ли такой элемент. Метка теперь стоит дважды.
$pull: убрать по значению
$pull удаляет из массива все элементы, равные значению. Обе копии «бестселлера» уйдут разом:
db.books.updateOne({ _id: 2 }, { $pull: { tags: 'бестселлер' } })
db.books.findOne({ _id: 2 }, { tags: 1 })
{
acknowledged: true,
insertedId: null,
matchedCount: 1,
modifiedCount: 1,
upsertedCount: 0
}
{ _id: 2, tags: [ 'классика', 'сатира' ] }
Вместо значения $pull принимает и условие. Например, { $pull: { reviews: { rating: { $lt: 3 } } } } удалит отзывы с оценкой ниже тройки. Это пригодится в конце урока.
$addToSet: добавить, если ещё нет
Для меток повторы не нужны. $addToSet добавляет элемент, только если его ещё нет в массиве:
db.books.updateOne({ _id: 2 }, { $addToSet: { tags: 'классика' } })
db.books.updateOne({ _id: 2 }, { $addToSet: { tags: { $each: ['классика', 'Москва'] } } })
db.books.findOne({ _id: 2 }, { tags: 1 })
{
acknowledged: true,
insertedId: null,
matchedCount: 1,
modifiedCount: 0,
upsertedCount: 0
}
{
acknowledged: true,
insertedId: null,
matchedCount: 1,
modifiedCount: 1,
upsertedCount: 0
}
{ _id: 2, tags: [ 'классика', 'сатира', 'Москва' ] }
«Классика» уже была, первая команда ничего не изменила. Модификатор $each позволяет передать сразу несколько элементов: добавилась только «Москва». Тот же $each работает и с $push.
$pop: снять с края
$pop удаляет один элемент с края массива: 1 последний, -1 первый. У «Мастера и Маргариты» метки ['классика', 'мистика', 'Москва']:
db.books.updateOne({ _id: 1 }, { $pop: { tags: 1 } })
db.books.updateOne({ _id: 1 }, { $pop: { tags: -1 } })
db.books.findOne({ _id: 1 }, { tags: 1 })
{
acknowledged: true,
insertedId: null,
matchedCount: 1,
modifiedCount: 1,
upsertedCount: 0
}
{
acknowledged: true,
insertedId: null,
matchedCount: 1,
modifiedCount: 1,
upsertedCount: 0
}
{ _id: 1, tags: [ 'мистика' ] }
Вместе $push и $pop превращают массив в стек или очередь, но для настоящих очередей задач лучше подходят другие инструменты, например Redis.
урок из курса
Урок из курса «MongoDB с нуля». Курс покупается один раз и остаётся навсегда: 500 ₽.
Оплата пока не подключена: премиум и курсы открываются промокодом.