Работа с массивами в документах

Как добавлять, удалять и обновлять элементы массива, не переписывая весь документ.

Массивы — родная часть документной модели: для их изменения есть отдельные операторы $push, $pull, $addToSet.

Зачем отдельные операторы

Пусть у пользователя есть массив тегов. Можно было бы прочитать документ, изменить массив в приложении и записать обратно — но это та же гонка, что и с числами. Операторы массивов меняют его прямо в базе, атомарно.

{
  "_id": "u1",
  "name": "Анна",
  "tags": ["js", "css"]
}

$push — добавить в конец

$push дописывает элемент в массив:

db.users.updateOne(
  { _id: "u1" },
  { $push: { tags: "mongodb" } }
)

Добавить сразу несколько — через $each:

db.users.updateOne(
  { _id: "u1" },
  { $push: { tags: { $each: ["node", "react"] } } }
)

$addToSet — без дубликатов

$push добавляет всегда, даже если такой элемент уже есть. Если массив должен хранить только уникальные значения (множество), используйте $addToSet — он добавит элемент, только если его там ещё нет:

db.users.updateOne(
  { _id: "u1" },
  { $addToSet: { tags: "js" } }   // "js" уже есть — массив не изменится
)

$pull — удалить по условию

$pull убирает из массива все элементы, подходящие под условие. Удалить тег css:

db.users.updateOne(
  { _id: "u1" },
  { $pull: { tags: "css" } }
)

Условие может быть и сложнее. Если массив состоит из чисел, удалить все меньше 10:

db.users.updateOne(
  { _id: "u1" },
  { $pull: { scores: { $lt: 10 } } }
)

Снять самый последний элемент поможет $pop (1 — с конца, -1 — с начала):

db.users.updateOne({ _id: "u1" }, { $pop: { tags: 1 } })

Обновить элемент массива объектов

Часто массив состоит из объектов — например, позиции заказа. Чтобы изменить совпавший элемент, используют позиционный оператор $: он указывает на элемент, который подошёл под фильтр. Увеличим количество у товара SKU-1001 внутри заказа:

db.orders.updateOne(
  { _id: "o1", "items.sku": "SKU-1001" },
  { $set: { "items.$.qty": 3 } }
)

Здесь items.$.qty читается как «у того элемента массива items, что совпал с фильтром, поле qty». Это ключевой приём для работы с вложенными массивами объектов.

Итог

  • $push добавляет элемент (с $each — несколько), $addToSet — только если его ещё нет.
  • $pull удаляет элементы по условию, $pop — крайний элемент массива.
  • Позиционный оператор $ обновляет элемент массива, совпавший с фильтром запроса.
Проверьте себя
1. Чем $addToSet отличается от $push?
A$addToSet добавляет элемент только если его ещё нет в массиве
B$addToSet удаляет элементы
C$addToSet работает только с числами
DРазличий нет
2. Какой оператор удаляет из массива элементы, подходящие под условие?
A$push
B$pull
C$set
D$inc
3. Что означает items.$.qty в обновлении?
AВсе элементы массива items
BПоле qty у элемента массива items, совпавшего с фильтром запроса
CПервый элемент массива всегда
DНовый элемент в конце массива
Поддержать проект