Перейти к содержимому

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

Archie Core предлагает мощные возможности группировки и агрегации через свой GraphQL API. Вы можете группировать записи по значениям полей, вычислять агрегатные функции (COUNT, SUM, AVG, MIN, MAX, COUNT_DISTINCT), фильтровать группы с помощью условий HAVING и сортировать результаты по агрегированным значениям.

При использовании группировки и агрегации следующие аргументы доступны в любом запросе списка:

АргументТипОписание
groupBy[{TableName}GroupBy]Поля для группировки результатов
aggregateBy[{TableName}AggregateInput]Агрегатные функции для вычисления
having[{TableName}HavingFilterInput]Фильтрация групп по результатам агрегации
aggregateSort[{TableName}AggregateSortInput]Сортировка результатов по агрегированным значениям

Эти аргументы можно комбинировать со стандартными аргументами, такими как filter, first, skip и orderBy.

Аргумент groupBy принимает массив значений enum, представляющих поля для группировки. Значения enum следуют шаблону: имя поля в camelCase, преобразованное в ВЕРХНИЙ_РЕГИСТР.

Соглашение об именовании:

Имя столбца (БД)Имя поля (GraphQL)Значение enum GroupBy
payment_methodpaymentMethodPAYMENTMETHOD
created_atcreatedAtCREATEDAT
statusstatusSTATUS
category_idcategoryIdCATEGORYID

Пример: Группировка студентов по активному статусу

Заголовок раздела «Пример: Группировка студентов по активному статусу»

Запрос

query {
students(groupBy: [ISACTIVE]) {
items {
isActive
}
count
}
}

Ответ

{
"data": {
"students": {
"items": [
{ "isActive": true },
{ "isActive": false }
],
"count": 2
}
}
}

Аргумент aggregateBy позволяет вычислять агрегатные функции над сгруппированными (или несгруппированными) данными. Каждая агрегация требует function, опциональное field и обязательный alias.

input {TableName}AggregateInput {
function: AggregateFunction!
field: {TableName}AggregateField
alias: String!
}
ФункцияОписаниеТребуется field?
COUNTПодсчёт строкНет (использует COUNT(*) при опускании)
SUMСуммирование числовых значенийДа
AVGВычисление среднегоДа
MINПоиск минимального значенияДа
MAXПоиск максимального значенияДа
COUNT_DISTINCTПодсчёт уникальных значенийДа

Enum field следует той же конвенции ВЕРХНЕГО_РЕГИСТРА, что и GroupBy.

alias — это строка, которую вы выбираете для именования результата. Она появляется как ключ в объекте ответа aggregates.

Пример: Подсчёт и средний возраст студентов, сгруппированных по активному статусу

Заголовок раздела «Пример: Подсчёт и средний возраст студентов, сгруппированных по активному статусу»

Запрос

query {
students(
groupBy: [ISACTIVE]
aggregateBy: [
{ function: COUNT, alias: "totalStudents" }
{ function: AVG, field: AGE, alias: "avgAge" }
]
) {
items {
isActive
}
count
aggregates
}
}

Ответ

{
"data": {
"students": {
"items": [
{ "isActive": true },
{ "isActive": false }
],
"count": 2,
"aggregates": [
{ "totalStudents": 5, "avgAge": 23.4 },
{ "totalStudents": 2, "avgAge": 21.0 }
]
}
}
}

Каждая запись в массиве aggregates соответствует группе с тем же индексом в items.

Аргумент having фильтрует группы по результатам агрегации, эквивалентно предложению SQL HAVING. Использует специальный тип ввода с тремя полями.

input {TableName}HavingFilterInput {
alias: String!
operator: HavingOperator!
value: Float!
}
  • alias: Должен совпадать с псевдонимом, определённым в aggregateBy.
  • operator: Оператор сравнения (см. таблицу ниже).
  • value: Числовой порог для сравнения.
ОператорЭквивалент SQL
EQUALS=
NOT_EQUALS!=
GREATER_THAN>
GREATER_THAN_OR_EQUAL>=
LESS_THAN<
LESS_THAN_OR_EQUAL<=

Запрос

query {
students(
groupBy: [CITYID]
aggregateBy: [
{ function: COUNT, alias: "studentCount" }
]
having: [
{ alias: "studentCount", operator: GREATER_THAN, value: 3 }
]
) {
items {
cityId
}
count
aggregates
}
}

Ответ

{
"data": {
"students": {
"items": [
{ "cityId": "e14638cb-6d72-4a36-b30f-9b763136a7bb" }
],
"count": 1,
"aggregates": [
{ "studentCount": 5 }
]
}
}
}

Возвращаются только группы, удовлетворяющие условию агрегации.

Аргумент aggregateSort сортирует сгруппированные результаты по агрегированному значению, а не по обычному полю.

input {TableName}AggregateSortInput {
alias: String!
direction: SortDirection!
}
  • alias: Должен совпадать с псевдонимом, определённым в aggregateBy.
  • direction: ASC (по возрастанию) или DESC (по убыванию).

Пример: Топ-3 города по количеству студентов

Заголовок раздела «Пример: Топ-3 города по количеству студентов»

Запрос

query {
students(
groupBy: [CITYID]
aggregateBy: [
{ function: COUNT, alias: "studentCount" }
]
aggregateSort: [
{ alias: "studentCount", direction: DESC }
]
first: 3
) {
items {
cityId
}
count
aggregates
}
}

Ответ

{
"data": {
"students": {
"items": [
{ "cityId": "e14638cb-6d72-4a36-b30f-9b763136a7bb" },
{ "cityId": "0174dc55-d494-4ebc-a0e9-13575461cad4" },
{ "cityId": "a2b3c4d5-e6f7-8901-2345-678901234567" }
],
"count": 3,
"aggregates": [
{ "studentCount": 5 },
{ "studentCount": 3 },
{ "studentCount": 2 }
]
}
}
}

Вы можете комбинировать filter, groupBy, aggregateBy, having, aggregateSort и first в одном запросе для сложного анализа.

Пример: Топ-5 городов активных студентов со средним возрастом выше 20, отсортированные по среднему возрасту

Заголовок раздела «Пример: Топ-5 городов активных студентов со средним возрастом выше 20, отсортированные по среднему возрасту»

Запрос

query {
students(
filter: { isActive: { equals: true } }
groupBy: [CITYID]
aggregateBy: [
{ function: COUNT, alias: "studentCount" }
{ function: AVG, field: AGE, alias: "avgAge" }
{ function: MAX, field: AGE, alias: "maxAge" }
]
having: [
{ alias: "avgAge", operator: GREATER_THAN, value: 20 }
]
aggregateSort: [
{ alias: "avgAge", direction: DESC }
]
first: 5
) {
items {
cityId
}
count
aggregates
}
}

Ответ

{
"data": {
"students": {
"items": [
{ "cityId": "e14638cb-6d72-4a36-b30f-9b763136a7bb" },
{ "cityId": "0174dc55-d494-4ebc-a0e9-13575461cad4" }
],
"count": 2,
"aggregates": [
{ "studentCount": 3, "avgAge": 24.3, "maxAge": 28 },
{ "studentCount": 2, "avgAge": 22.5, "maxAge": 25 }
]
}
}
}

Этот запрос:

  1. Фильтрует только активных студентов (filter).
  2. Группирует по городу (groupBy).
  3. Вычисляет количество, средний возраст и максимальный возраст в каждой группе (aggregateBy).
  4. Сохраняет только группы со средним возрастом выше 20 (having).
  5. Сортирует по среднему возрасту по убыванию (aggregateSort).
  6. Ограничивает до топ-5 групп (first).

При использовании агрегации ответ следует стандартному типу Connection с дополнительным полем aggregates:

ПолеТипОписание
items[{TableName}!]!Сгруппированные записи (по одной на группу, с значениями сгруппированных полей)
countInt!Количество возвращённых групп
pageInfoPageInfo!Информация о пагинации (hasNextPage, hasPreviousPage)
aggregatesJSONМассив объектов, каждый содержит вычисленные агрегированные значения, индексированные по псевдониму

Массив aggregates параллелен items — агрегат с индексом i соответствует группе с индексом i в items.

  • Значения enum GroupBy и AggregateField используют ВЕРХНИЙ_РЕГИСТР имени поля в camelCase: payment_methodpaymentMethodPAYMENTMETHOD.
  • Псевдонимы having должны совпадать с псевдонимом, определённым в aggregateBy. Если не совпадают, условие having игнорируется.
  • Псевдонимы aggregateSort должны совпадать с псевдонимом, определённым в aggregateBy.
  • COUNT без field использует COUNT(*), подсчитывая все строки в группе.
  • Несколько условий having можно комбинировать — все должны выполняться (логика AND).
  • Несколько записей aggregateSort применяются в порядке приоритета (первая — основной порядок сортировки).
  • Стандартные аргументы пагинации (first, skip, after, before) работают со сгруппированными результатами.
  • Аргумент filter применяется до группировки (эквивалент SQL WHERE), а having — после группировки (эквивалент SQL HAVING).