그룹화 및 집계
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
섹션 제목: “GroupBy”groupBy 인수는 그룹화할 필드를 나타내는 enum 값의 배열을 받습니다. enum 값은 camelCase 필드 이름을 대문자로 변환한 패턴을 따릅니다.
명명 규칙:
| 열 이름 (DB) | 필드 이름 (GraphQL) | GroupBy enum 값 |
|---|---|---|
payment_method | paymentMethod | PAYMENTMETHOD |
created_at | createdAt | CREATEDAT |
status | status | STATUS |
category_id | categoryId | CATEGORYID |
예시: 활성 상태별 학생 그룹화
섹션 제목: “예시: 활성 상태별 학생 그룹화”요청
query { students(groupBy: [ISACTIVE]) { items { isActive } count }}응답
{ "data": { "students": { "items": [ { "isActive": true }, { "isActive": false } ], "count": 2 } }}AggregateBy
섹션 제목: “AggregateBy”aggregateBy 인수는 그룹화된(또는 그룹화되지 않은) 데이터에 대해 집계 함수를 계산할 수 있게 합니다. 각 집계에는 function, 선택적 field, 필수 alias가 필요합니다.
AggregateInput 구조
섹션 제목: “AggregateInput 구조”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
섹션 제목: “Having”having 인수는 집계 결과를 기준으로 그룹을 필터링하며, SQL의 HAVING 절과 동일합니다. 세 개의 필드가 있는 전용 입력 유형을 사용합니다.
HavingFilterInput 구조
섹션 제목: “HavingFilterInput 구조”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 | <= |
예시: 학생이 3명 이상인 도시
섹션 제목: “예시: 학생이 3명 이상인 도시”요청
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
섹션 제목: “AggregateSort”aggregateSort 인수는 일반 필드가 아닌 집계 값으로 그룹화된 결과를 정렬합니다.
AggregateSortInput 구조
섹션 제목: “AggregateSortInput 구조”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 } ] } }}이 쿼리는:
- 필터링 활성 학생만 (
filter). - 그룹화 도시별 (
groupBy). - 계산 각 그룹의 개수, 평균 나이, 최대 나이 (
aggregateBy). - 유지 평균 나이가 20 초과인 그룹만 (
having). - 정렬 평균 나이 내림차순 (
aggregateSort). - 제한 상위 5개 그룹 (
first).
응답 구조
섹션 제목: “응답 구조”집계를 사용할 때 응답은 추가 aggregates 필드가 있는 표준 Connection 유형을 따릅니다:
| 필드 | 유형 | 설명 |
|---|---|---|
items | [{TableName}!]! | 그룹화된 레코드 (그룹당 하나, 그룹화된 필드 값 포함) |
count | Int! | 반환된 그룹 수 |
pageInfo | PageInfo! | 페이지네이션 정보 (hasNextPage, hasPreviousPage) |
aggregates | JSON | 각각 alias로 인덱싱된 계산된 집계 값을 포함하는 객체 배열 |
aggregates 배열은 items와 병렬입니다 — 인덱스 i의 집계는 items의 인덱스 i에 있는 그룹에 해당합니다.
중요 사항
섹션 제목: “중요 사항”- GroupBy 및 AggregateField enum 값은 camelCase 필드 이름의 대문자를 사용합니다:
payment_method→paymentMethod→PAYMENTMETHOD. - having alias는
aggregateBy에 정의된 alias와 일치해야 합니다. 일치하지 않으면 having 조건이 무시됩니다. - aggregateSort alias는
aggregateBy에 정의된 alias와 일치해야 합니다. - field 없이 COUNT는
COUNT(*)를 사용하여 그룹의 모든 행을 셉니다. - 여러 having 조건을 결합할 수 있습니다 — 모두 충족되어야 합니다 (AND 논리).
- 여러 aggregateSort 항목은 우선순위 순서로 적용됩니다 (첫 번째 항목이 기본 정렬).
- 표준 페이지네이션 인수 (
first,skip,after,before)는 그룹화된 결과와 함께 작동합니다. - filter 인수는 그룹화 전에 적용됩니다 (SQL WHERE에 해당), having은 그룹화 후에 적용됩니다 (SQL HAVING에 해당).