Эта глава представляет подробное объяснение статистического анализа MongoDB, в основном осуществляемого через Aggregation Pipeline. Это похоже на оператор "group by" в SQL. В оболочке MongoDB статистический анализ осуществляется с использованием функции db.collection.aggregate().

Обучающий курс

MongoDB Aggregation Pipeline

Общие шаги

  • Используйте $match для фильтрации целевых данных
  • Используйте $group для группировки и вычисления данных
  • Используйте $sort для сортировки результатов (необязательно)

Тестовые данные

Данные в коллекции orders выглядят следующим образом

{ _id: 1, cust_id: "abc1", ord_date: ISODate("2012-11-02T17:04:11.102Z"), status: "A", amount: 50 }
{ _id: 2, cust_id: "xyz1", ord_date: ISODate("2013-10-01T17:04:11.102Z"), status: "A", amount: 100 }
{ _id: 3, cust_id: "xyz1", ord_date: ISODate("2013-10-12T17:04:11.102Z"), status: "D", amount: 25 }
{ _id: 4, cust_id: "xyz1", ord_date: ISODate("2013-10-11T17:04:11.102Z"), status: "D", amount: 125 }
{ _id: 5, cust_id: "abc1", ord_date: ISODate("2013-11-12T17:04:11.102Z"), status: "A", amount: 25 }

Функция aggregate

db.collection.aggregate(pipeline) Объяснение:

  • pipeline принимает массив в качестве параметра, где каждый элемент представляет собой этап обработки.

Пример

db.orders.aggregate([
                     { $match: { status: "A" } },  // Первый этап
                     { $group: { _id: "$cust_id", total: { $sum: "$amount" } } },   // Второй этап
                     { $sort: { total: -1 } }  // Третий этап
                   ])

Эквивалентный SQL

select sum(amount) as total from orders 
        where status="A" 
        group by cust_id 
        order by total desc

Этап $match

Формат:

{ $match: { <query> } }

Объяснение:

  • <query> - Условия запроса MongoDB

Используется для установки условий запроса. Если $match игнорируется, это подразумевает запрос всех данных.

Совет: Если вы не знакомы с синтаксисом запросов MongoDB, пожалуйста, обратитесь к предыдущим главам.

Этап $group

Похожий на ключевое слово group by в SQL, используется для группировки данных, после чего выполняются ряд статистических расчетов над сгруппированными данными.

Базовое использование $group

Синтаксис:

{
  $group:
    {
      _id: <выражение>, // Условие группировки, например: по какому полю группировать
      <поле1>: { <аккумулятор1> : <выражение1> },  // Операция агрегации, можно добавить N агрегационных операций
      ...
    }
 }

Объяснение:

  • <поле1> - Название пользовательского статистического показателя, может быть N в общей сложности
  • <аккумулятор1> - Агрегатная функция, аналогичная sum, avg и другим агрегатным функциям в SQL, отличие заключается в том, что агрегатные функции MongoDB называются с префиксом $, например: $sum, $avg
  • <выражение1> - Параметр агрегатной функции, обычно это значение поля, которое нужно посчитать, ссылка на поля документа осуществляется в формате "$название поля"

Пример:

db.orders.aggregate([
                     {
					 	$group: { 
							_id: "$cust_id",
							total: { $sum: "$amount" }, // Добавить первый расчитанный показатель total, используя оператор суммирования $sum
							amount_avg: {$avg: "$amount"}  // Добавить второй расчитанный показатель avg, используя оператор расчета среднего значения $avg
						} 
					}
               ])

Результат:

{ "_id" : "abc1", "total" : 75, "amount_avg" : 37.5 }
{ "_id" : "xyz1", "total" : 250, "amount_avg" : 83.33333333333333 }

Эквивалентный SQL:

select 
	sum(amount) as  total,
	avg(amount) as amount_avg
from orders 
group by cust_id

Функции агрегации $group

Часто используемые функции агрегации для $group:

Оператор Описание Пример
$avg Рассчитать среднее значение {$avg: "$amount"}
$sum Суммирование {$sum: "$amount"}
$max Максимальное значение {$max: "$amount"}
$min Минимальное значение {$min: "$amount"}
$first Вернуть данные после группировки, содержимое первого документа {$first: "$amount"}
$last Вернуть данные после группировки, содержимое последнего документа {$last: "$amount"}
$push Вернуть данные после группировки { $push: { ord_date: "$ord_date", amount: "$amount" }
$addToSet Вернуть данные после группировки, отличается от $push тем, что удаляет дубликаты { $addToSet: "$amount" }

Пример $push

db.orders.aggregate(
   [
     {
       $group:
         {
           _id: "$cust_id",
           all: { $push: { ord_date: "$ord_date", amount: "$amount" } } // Значения полей ord_date и amount
         }
     }
   ]
)

Вывод

{ "_id" : "abc1", "all" : [ { "ord_date" : "2021-04-18 00:00:00", "amount" : 50 }, { "ord_date" : "2021-04-21 00:00:00", "amount" : 25 } ] }
{ "_id" : "xyz1", "all" : [ { "ord_date" : "2021-04-18 00:00:00", "amount" : 100 }, { "ord_date" : "2021-04-20 00:00:00", "amount" : 25 }, { "ord_date" : "2021-04-21 00:00:00", "amount" : 125 } ] }

Пример $addToSet

db.orders.aggregate(
   [
     {
       $group:
         {
           _id: "$cust_id",
           all_amount: { $addToSet: "$amount" } // Возвращает все уникальные значения amount
         }
     }
   ]
)

Вывод

{ "_id" : "abc1", "all_amount" : [ 25, 50 ] }
{ "_id" : "xyz1", "all_amount" : [ 100, 25, 125 ] }

$sort:

Этап $sort обычно размещается в конце для сортировки агрегированных данных.

Формат:

{ $sort: { <поле1>: <порядок сортировки>, <поле2>: <порядок сортировки> ... } }

Объяснение:

  • <поле1>, <поле2> - Названия полей, которые нужно отсортировать, поддерживает несколько полей.
  • <порядок сортировки> - Направление сортировки, -1 для по убыванию, 1 для по возрастанию.

Пример:

db.orders.aggregate([
                     { $match: { status: "A" } },
                     { $group: { _id: "$cust_id", total: { $sum: "$amount" } } },
                     { $sort: { total: -1 } }
                   ])

Агрегатная пагинация

Мы можем реализовать пагинацию с помощью операторов $limit и $skip.

Пример:

db.orders.aggregate([
                     { $match: { status: "A" } },
                     { $group: { _id: "$cust_id", total: { $sum: "$amount" } } },
                     { $sort: { total: -1 } },
					 { $limit: 5 }, // Ограничивает количество возвращаемых записей, аналогично размеру страницы в пагинации.
					 { $skip: 1 } // Пропускает определенное количество записей, аналогично смещению в SQL.
                   ])