Ir al contenido

Agrupación y Agregaciones

Archie Core ofrece potentes capacidades de agrupación y agregación a través de su API GraphQL. Puedes agrupar registros por valores de campos, calcular funciones de agregación (COUNT, SUM, AVG, MIN, MAX, COUNT_DISTINCT), filtrar grupos con condiciones HAVING y ordenar resultados por valores agregados.

Al usar agrupación y agregaciones, los siguientes argumentos están disponibles en cualquier consulta de lista:

ArgumentoTipoDescripción
groupBy[{TableName}GroupBy]Campos por los que agrupar resultados
aggregateBy[{TableName}AggregateInput]Funciones de agregación a calcular
having[{TableName}HavingFilterInput]Filtrar grupos por resultados agregados
aggregateSort[{TableName}AggregateSortInput]Ordenar resultados por valores agregados

Estos argumentos pueden combinarse con argumentos estándar como filter, first, skip y orderBy.

El argumento groupBy acepta un array de valores enum que representan los campos por los que agrupar. Los valores enum siguen el patrón: el nombre del campo en camelCase convertido a MAYÚSCULAS.

Convención de nombres:

Nombre de columna (DB)Nombre de campo (GraphQL)Valor enum GroupBy
payment_methodpaymentMethodPAYMENTMETHOD
created_atcreatedAtCREATEDAT
statusstatusSTATUS
category_idcategoryIdCATEGORYID

Ejemplo: Agrupar estudiantes por estado activo

Sección titulada «Ejemplo: Agrupar estudiantes por estado activo»

Solicitud

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

Respuesta

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

El argumento aggregateBy permite calcular funciones de agregación sobre datos agrupados (o no agrupados). Cada agregado requiere una function, un field opcional y un alias obligatorio.

input {TableName}AggregateInput {
function: AggregateFunction!
field: {TableName}AggregateField
alias: String!
}
FunciónDescripción¿field requerido?
COUNTContar filasNo (usa COUNT(*) cuando se omite)
SUMSumar valores numéricos
AVGCalcular promedio
MINEncontrar valor mínimo
MAXEncontrar valor máximo
COUNT_DISTINCTContar valores distintos

El enum field sigue la misma convención MAYÚSCULAS que GroupBy.

El alias es una cadena que eliges para nombrar el resultado. Aparece como clave en el objeto de respuesta aggregates.

Ejemplo: Contar y promediar edad de estudiantes agrupados por estado activo

Sección titulada «Ejemplo: Contar y promediar edad de estudiantes agrupados por estado activo»

Solicitud

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

Respuesta

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

Cada entrada en el array aggregates corresponde al grupo en el mismo índice en items.

El argumento having filtra grupos según los resultados agregados, equivalente a la cláusula SQL HAVING. Utiliza un tipo de entrada dedicado con tres campos.

input {TableName}HavingFilterInput {
alias: String!
operator: HavingOperator!
value: Float!
}
  • alias: Debe coincidir con un alias definido en aggregateBy.
  • operator: El operador de comparación (ver tabla abajo).
  • value: El umbral numérico contra el que comparar.
OperadorEquivalente SQL
EQUALS=
NOT_EQUALS!=
GREATER_THAN>
GREATER_THAN_OR_EQUAL>=
LESS_THAN<
LESS_THAN_OR_EQUAL<=

Solicitud

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

Respuesta

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

Solo se devuelven los grupos que cumplen la condición de agregación.

El argumento aggregateSort ordena los resultados agrupados por un valor agregado, en lugar de por un campo regular.

input {TableName}AggregateSortInput {
alias: String!
direction: SortDirection!
}
  • alias: Debe coincidir con un alias definido en aggregateBy.
  • direction: ASC (ascendente) o DESC (descendente).

Ejemplo: Top 3 ciudades por número de estudiantes

Sección titulada «Ejemplo: Top 3 ciudades por número de estudiantes»

Solicitud

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

Respuesta

{
"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 }
]
}
}
}

Puedes combinar filter, groupBy, aggregateBy, having, aggregateSort y first en una sola consulta para análisis complejos.

Ejemplo: Top 5 ciudades de estudiantes activos con edad promedio superior a 20, ordenadas por edad promedio

Sección titulada «Ejemplo: Top 5 ciudades de estudiantes activos con edad promedio superior a 20, ordenadas por edad promedio»

Solicitud

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
}
}

Respuesta

{
"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 }
]
}
}
}

Esta consulta:

  1. Filtra solo estudiantes activos (filter).
  2. Agrupa por ciudad (groupBy).
  3. Calcula conteo, edad promedio y edad máxima en cada grupo (aggregateBy).
  4. Mantiene solo grupos con edad promedio superior a 20 (having).
  5. Ordena por edad promedio descendente (aggregateSort).
  6. Limita a los 5 grupos principales (first).

Al usar agregaciones, la respuesta sigue el tipo Connection estándar con un campo adicional aggregates:

CampoTipoDescripción
items[{TableName}!]!Los registros agrupados (uno por grupo, con los valores de los campos agrupados)
countInt!Número de grupos devueltos
pageInfoPageInfo!Info de paginación (hasNextPage, hasPreviousPage)
aggregatesJSONArray de objetos, cada uno con los valores agregados calculados indexados por alias

El array aggregates es paralelo a items — el agregado en el índice i corresponde al grupo en el índice i en items.

  • Los valores enum GroupBy y AggregateField usan MAYÚSCULAS del nombre del campo en camelCase: payment_methodpaymentMethodPAYMENTMETHOD.
  • Los alias de having deben coincidir con un alias definido en aggregateBy. Si no coinciden, la condición having se ignora.
  • Los alias de aggregateSort deben coincidir con un alias definido en aggregateBy.
  • COUNT sin field usa COUNT(*), contando todas las filas del grupo.
  • Múltiples condiciones having pueden combinarse — todas deben cumplirse (lógica AND).
  • Múltiples entradas aggregateSort se aplican en orden de prioridad (la primera es el orden principal).
  • Los argumentos de paginación estándar (first, skip, after, before) funcionan con resultados agrupados.
  • El argumento filter se aplica antes de agrupar (equivalente a SQL WHERE), mientras que having se aplica después de agrupar (equivalente a SQL HAVING).