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

Конвейер агрегации: $match, $group, $sort

junior~20 мин

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

В прошлом уроке мы закончили моделирование: данные разложены, коллекции защищены правилами. Теперь по этим данным хочется считать. Сколько книг каждого жанра? Какая средняя цена? Кто из покупателей заказывает чаще всех? find на такие вопросы не отвечает: он возвращает документы, а не итоги. Итоги считает конвейер агрегации. Примеры идут на учебной базе магазина.

Сортировочная линия

Представь сортировочный центр почты. Посылки едут по ленте мимо нескольких постов. На первом посту отбрасывают всё, что не в Москву. На втором раскладывают оставшееся по районам и считают коробки в каждом. На третьем выстраивают районы по числу коробок. Каждый пост получает то, что отдал предыдущий, и делает одно простое действие.

Конвейер агрегации устроен так же. Это массив этапов, и документы коллекции проходят их по очереди:

books ─▶ $match ─▶ $group ─▶ $sort ─▶ результат
         отбор     итоги     порядок

Запускает конвейер метод aggregate:

db.коллекция.aggregate([
  { этап1 },
  { этап2 },
  ...
])

$group: итоги по группам

Начнём с главного этапа. $group раскладывает документы по группам и для каждой группы выдаёт один документ с итогами. Сколько книг каждого жанра:

db.books.aggregate([
  { $group: { _id: '$genre', books: { $sum: 1 } } },
  { $sort: { books: -1, _id: 1 } }
])
[
  { _id: 'повесть', books: 3 },
  { _id: 'роман', books: 2 },
  { _id: 'учебник', books: 2 },
  { _id: 'фантастика', books: 2 }
]

Разберём, что написано в $group:

  • _id: '$genre' говорит, по чему группировать. Знак доллара перед именем поля значит «возьми значение этого поля из документа». Без доллара 'genre' была бы обычной строкой, и все книги попали бы в одну группу с именем genre.
  • books: { $sum: 1 } это новое поле в выходном документе. Аккумулятор $sum прибавляет единицу за каждый документ группы, то есть считает документы.

Порядок групп после $group сервер не обещает, поэтому дальше стоит $sort. Второй ключ _id: 1 разводит жанры с одинаковым числом книг по алфавиту.

Если ты прошёл урок про агрегаты в PostgreSQL, этот запрос тебе знаком:

SELECT genre AS _id, count(*) AS books
FROM books
GROUP BY genre
ORDER BY books DESC, genre;

урок из курса

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

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