콘텐츠로 이동

그룹화 및 집계

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 필드 이름을 대문자로 변환한 패턴을 따릅니다.

명명 규칙:

열 이름 (DB)필드 이름 (GraphQL)GroupBy enum 값
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고유 값 개수 세기

field enum은 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에 정의된 alias와 일치해야 합니다.
  • 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에 정의된 alias와 일치해야 합니다.
  • 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를 결합할 수 있습니다.

예시: 평균 나이가 20 초과인 활성 학생의 상위 5개 도시, 평균 나이순 정렬

섹션 제목: “예시: 평균 나이가 20 초과인 활성 학생의 상위 5개 도시, 평균 나이순 정렬”

요청

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).

집계를 사용할 때 응답은 추가 aggregates 필드가 있는 표준 Connection 유형을 따릅니다:

필드유형설명
items[{TableName}!]!그룹화된 레코드 (그룹당 하나, 그룹화된 필드 값 포함)
countInt!반환된 그룹 수
pageInfoPageInfo!페이지네이션 정보 (hasNextPage, hasPreviousPage)
aggregatesJSON각각 alias로 인덱싱된 계산된 집계 값을 포함하는 객체 배열

aggregates 배열은 items와 병렬입니다 — 인덱스 i의 집계는 items의 인덱스 i에 있는 그룹에 해당합니다.

  • GroupBy 및 AggregateField enum 값은 camelCase 필드 이름의 대문자를 사용합니다: payment_methodpaymentMethodPAYMENTMETHOD.
  • having aliasaggregateBy에 정의된 alias와 일치해야 합니다. 일치하지 않으면 having 조건이 무시됩니다.
  • aggregateSort aliasaggregateBy에 정의된 alias와 일치해야 합니다.
  • field 없이 COUNTCOUNT(*)를 사용하여 그룹의 모든 행을 셉니다.
  • 여러 having 조건을 결합할 수 있습니다 — 모두 충족되어야 합니다 (AND 논리).
  • 여러 aggregateSort 항목은 우선순위 순서로 적용됩니다 (첫 번째 항목이 기본 정렬).
  • 표준 페이지네이션 인수 (first, skip, after, before)는 그룹화된 결과와 함께 작동합니다.
  • filter 인수는 그룹화 전에 적용됩니다 (SQL WHERE에 해당), having은 그룹화 후에 적용됩니다 (SQL HAVING에 해당).