Совокупный

Описание

На этапе aggregate(...) вычисляются агрегированные результаты (например, количество, сумма) на основе документов, возвращенных на предыдущем этапе.

При наличии выражения группировки, если оно задано, документы группируются на основе предоставленных выражений, а затем к каждой группе применяются функции-аккумуляторы.

Примеры

Для агрегаций без группировки этап aggregate(...) принимает одно или несколько псевдонимных выражений агрегатора:

Node.js

const cities = await db.pipeline()
  .collection("/cities")
  .aggregate(
      countAll().as("total"),
      average("population").as("avg_population")
  )
  .execute();

Для агрегирования с группировкой, помимо агрегаторов, требуются дополнительные группы:

Node.js

const result = await db.pipeline()
  .collectionGroup('citites')
  .aggregate({
    accumulators: [
      countAll().as('cities'),
      field('population').sum().as('total_popoluation')
    ],
    groups: [field('location.state').as('state')]
  })
  .execute();

Поведение

Агрегации без группировки

Создайте коллекцию cities , содержащую следующие документы:

Node.js

await db.collection('cities').doc('SF').set({name: 'San Francisco', state: 'CA', country: 'USA', population: 870000});
await db.collection('cities').doc('LA').set({name: 'Los Angeles', state: 'CA', country: 'USA', population: 3970000});
await db.collection('cities').doc('NY').set({name: 'New York', state: 'NY', country: 'USA', population: 8530000});
await db.collection('cities').doc('TOR').set({name: 'Toronto', state: null, country: 'Canada', population: 2930000});
await db.collection('cities').doc('MEX').set({name: 'Mexico City', state: null, country: 'Mexico', population: 9200000});

Чтобы узнать общее количество городов и их среднюю численность населения:

Node.js

const cities = await db.pipeline()
  .collection("/cities")
  .aggregate(
      countAll().as("total"),
      average("population").as("avg_population")
  )
  .execute();

который производит:

{avg_population: 5100000, total: 5}

Выполнять агрегирование по группам

Указав аргумент groups , вы можете выполнять агрегирование по каждой отдельной группе.

Например, чтобы найти город с наибольшим населением в каждой стране и каждом штате:

Node.js

const cities = await db.pipeline()
  .collection("/cities")
  .aggregate({
      accumulators: [
          countAll().as("number_of_cities"),
          maximum("population").as("max_population")
      ],
      groups: ["country", "state"]
  })
  .execute();

что дает:

{country: "USA", state: "CA", max_population: 3970000, number_of_cities: 2},
{country: "USA", state: "NY", max_population: 8530000, number_of_cities: 1},
{country: "Canada", state: null, max_population: 2930000, number_of_cities: 1},
{country: "Mexico", state: null,  max_population: 9200000, number_of_cities: 1}

Сложные выражения при группировке

Помимо группировки только по значениям полей, этап aggregate(...) поддерживает группировку по результатам сложных выражений. Любое выражение, допустимое на этапе select , может использоваться в качестве ключа группировки. Это позволяет гибко группировать данные на основе вычисленных значений или условий.

Например, чтобы сгруппировать данные по тому, является ли поле «штат» нулевым, и узнать общую численность населения в каждой группе:

Node.js

const cities = await db.pipeline()
  .collection("/cities")
  .aggregate({
      accumulators: [
         sum("population").as("total_population")
      ],
      groups: [equal(field("state"), null).as("state_is_null")]
  })
  .execute();

вернется:

{state_is_null: true, total_population: 12130000}
{state_is_null: false, total_population: 13370000}

Поведение агрегаторов

Описание поведения каждой поддерживаемой функции (например, count , sum , avg ) при выполнении агрегирующих операций можно найти на отдельной странице, посвященной агрегирующим функциям .

Ключевые модели поведения группы

При группировке документов Firestore использует семантику равенства для определения того, принадлежат ли значения к одной группе.

Это означает, что эквивалентные значения, например, математически эквивалентные числовые значения, независимо от исходного типа (32-битное целое число, 64-битное целое число, числа с плавающей запятой, decimal128 и т. д.), группируются вместе.

Например, в коллекции numerics , содержащей различные документы со значениями foo , представляющими собой 32-битное целое число 1 , 64-битное целое число 1L и число с плавающей запятой 1.0 соответственно, все они будут объединены в одну группу. Выполнение команды count grouping by foo вернет:

{foo: 1.0, count: 3}

В случаях, когда в наборе данных присутствуют различные эквивалентные значения, выходное значение группы может быть любым из этих эквивалентных значений. В этом примере foo может быть 1 , 1L или 1.0 .

Даже если кажется, что результат предсказуем, не следует полагаться на поведение какого-либо конкретного выбранного значения.

Использование памяти

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

В случае очень большого количества групп или если каждая группа очень велика (например, группировка по огромным значениям), на этом этапе может закончиться память.

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