Skip to main content

Aggregations

When aggregations are enabled nestjs-query will expose a new query that enables using the following common aggregations count, avg, sum, min, and max.

note

Only fields decorated with @FilterableField will be exposed in aggregate queries.

All examples will be based on the following TodoItem DTO. All fields except age, because it is not decorated with @FilterableField or @IDField, will be exposed in aggregate queries.

todo-item/dto/todo-item.dto.ts
import { FilterableField, IDField } from '@ptc-org/nestjs-query-graphql';
import { ObjectType, ID, GraphQLISODateTime, Field } from '@nestjs/graphql';

@ObjectType('TodoItem')
export class TodoItemDTO {
@IDField(() => ID)
id!: number;

@FilterableField()
title!: string;

@FilterableField({ nullable: true })
description?: string;

@FilterableField()
completed!: boolean;

@FilterableField(() => GraphQLISODateTime)
created!: Date;

@FilterableField(() => GraphQLISODateTime)
updated!: Date;

@Field()
age!: number;

@FilterableField()
priority!: number;
}

Enabling Aggregate Queries

To enable aggregate queries you can set the enableAggregate option in your resolver

todo-item/todo-item.module.ts
import { NestjsQueryGraphQLModule } from '@ptc-org/nestjs-query-graphql';
import { NestjsQueryTypeOrmModule } from '@ptc-org/nestjs-query-typeorm';
import { Module } from '@nestjs/common';
import { TodoItemInputDTO } from './dto/todo-item-input.dto';
import { TodoItemUpdateDTO } from './dto/todo-item-update.dto';
import { TodoItemDTO } from './dto/todo-item.dto';
import { TodoItemEntity } from './todo-item.entity';

@Module({
imports: [
NestjsQueryGraphQLModule.forFeature({
imports: [NestjsQueryTypeOrmModule.forFeature([TodoItemEntity])],
resolvers: [
{
DTOClass: TodoItemDTO,
EntityClass: TodoItemEntity,
CreateDTOClass: TodoItemInputDTO,
UpdateDTOClass: TodoItemUpdateDTO,
enableAggregate: true
},
],
}),
],
})
export class TodoItemModule {}

All aggregate queries use the following naming convention ${objectName}Aggregate.

Below is a fragment from the generated schema for TodoItem

info

nestjs-query will only expose number fields for avg and sum.

type Query {
todoItem(id: ID!): TodoItem
todoItems(
paging: CursorPaging = { first: 10 }

filter: TodoItemFilter = {}

sorting: [TodoItemSort!] = []
): TodoItemConnection!
todoItemAggregate(filter: TodoItemAggregateFilter): [TodoItemAggregateResponse!]!
}

input TodoItemAggregateFilter {
and: [TodoItemAggregateFilter!]
or: [TodoItemAggregateFilter!]
id: IDFilterComparison
title: StringFieldComparison
description: StringFieldComparison
completed: BooleanFieldComparison
created: DateFieldComparison
updated: DateFieldComparison
priority: NumberFieldComparison
}

type TodoItemAggregateResponse {
groupBy: TodoItemAggregateGroupBy
count: TodoItemCountAggregate
sum: TodoItemSumAggregate
avg: TodoItemAvgAggregate
min: TodoItemMinAggregate
max: TodoItemMaxAggregate
}

type TodoItemAvgAggregate {
id: Float
priority: Float
}

type TodoItemAggregateGroupBy {
id: ID
title: String
description: String
completed: Boolean
created(by: GroupBy = DAY): DateTime
updated(by: GroupBy = DAY): DateTime
priority: Float
createdBy: String
updatedBy: String
}

type TodoItemCountAggregate {
id: Int
title: Int
description: Int
completed: Int
created: Int
updated: Int
priority: Int
}

type TodoItemMaxAggregate {
id: ID
title: String
description: String
created: DateTime
updated: DateTime
priority: Float
}

type TodoItemMinAggregate {
id: ID
title: String
description: String
created: DateTime
updated: DateTime
priority: Float
}

type TodoItemSumAggregate {
id: Float
priority: Float
}

Examples

Basic

{
todoItemAggregate {
count {
id
}
sum {
id
}
avg {
id
}
min {
id
title
created
}
max {
id
title
created
}
}
}

With GroupBy

To group your aggregate queries you can add a groupBy to specify one or more fields to group on.

{
todoItemAggregate {
groupBy {
completed
}
count {
id
}
sum {
id
}
avg {
id
}
min {
id
title
created
}
max {
id
title
created
}
}
}

With Filter

You can also provide a filter to only aggregate on a subset of data.

{
todoItemAggregate(filter: { completed: { is: false } }) {
count {
id
}
min {
id
title
created
}
max {
id
title
created
}
}
}

When using the count aggregate only non-null fields will be counted.

For example assume description is null for all todo items you will get 0 back.

{
todoItemAggregate(filter: { completed: { is: false } }) {
count {
id
title
description
}
}
}

Aggregating Relations

When using the enableAggregate option any defined many relations will also expose a aggregate query {relationName}Aggregate

Building on the previous example assume TodoItem has a subTasks connection.

The following schema fragment will be created

type TodoItem {
id: ID!
title: String!
description: String
completed: Boolean!
created: DateTime!
updated: DateTime!
age: Float!
priority: Float!
subTasks(
paging: CursorPaging = { first: 10 }

filter: SubTaskFilter = {}

sorting: [SubTaskSort!] = []
): TodoItemSubTasksConnection!
subTasksAggregate(
filter: SubTaskAggregateFilter
):[ TodoItemSubTasksAggregateResponse!]!
}

type TodoItemSubTasksAggregateResponse {
groupBy: TodoItemSubTasksAggregateGroupBy
count: TodoItemSubTasksCountAggregate
sum: TodoItemSubTasksSumAggregate
avg: TodoItemSubTasksAvgAggregate
min: TodoItemSubTasksMinAggregate
max: TodoItemSubTasksMaxAggregate
}

type TodoItemSubTasksAggregateGroupBy {
id: ID
title: String
description: String
completed: Boolean
created: DateTime
updated: DateTime
todoItemId: String
createdBy: String
updatedBy: String
}

type TodoItemSubTasksAvgAggregate {
id: Float
}

type TodoItemSubTasksCountAggregate {
id: Int
title: Int
description: Int
completed: Int
created: Int
updated: Int
todoItemId: Int
}

type TodoItemSubTasksMaxAggregate {
id: ID
title: String
description: String
created: DateTime
updated: DateTime
todoItemId: String
}

type TodoItemSubTasksMinAggregate {
id: ID
title: String
description: String
created: DateTime
updated: DateTime
todoItemId: String
}

type TodoItemSubTasksSumAggregate {
id: Float
}

Examples

Basic

In this example we'll aggregate on all related subTasks.

{
todoItem(id: 5) {
subTasksAggregate {
count {
id
}
sum {
id
}
avg {
id
}
min {
id
title
}
max {
id
title
}
}
}
}

With GroupBy

In this example we'll aggregate on all related subTasks and group by completed.

{
todoItem(id: 5) {
subTasksAggregate {
groupBy {
completed
}
count {
id
}
sum {
id
}
avg {
id
}
min {
id
title
}
max {
id
title
}
}
}
}

With Filter

This example will aggregate all related subTasks that are not completed.

{
todoItem(id: 5) {
subTasksAggregate(filter: { completed: { is: false } }) {
count {
id
}
min {
id
title
}
max {
id
title
}
}
}
}

Advanced

Enabling Aggregates Only For Root

When using the enableAggregate option it will enable aggregates on the root type as well as all relations. If you only want to expose aggregate functionality on the root type you can specify the aggregate option.

import { NestjsQueryGraphQLModule } from '@ptc-org/nestjs-query-graphql'
import { NestjsQueryTypeOrmModule } from '@ptc-org/nestjs-query-typeorm'
import { Module } from '@nestjs/common'
import { TodoItemInputDTO } from './dto/todo-item-input.dto'
import { TodoItemUpdateDTO } from './dto/todo-item-update.dto'
import { TodoItemDTO } from './dto/todo-item.dto'
import { TodoItemEntity } from './todo-item.entity'

@Module({
imports: [
NestjsQueryGraphQLModule.forFeature({
imports: [NestjsQueryTypeOrmModule.forFeature([TodoItemEntity])],
resolvers: [
{
DTOClass: TodoItemDTO,
EntityClass: TodoItemEntity,
CreateDTOClass: TodoItemInputDTO,
UpdateDTOClass: TodoItemUpdateDTO,
aggregate: { enabled: true }
}
]
})
]
})
export class TodoItemModule {}

Disable Aggregate for Single Relation

You can also selectively disable aggregates on an individual relation by specifying the enableAggregate option when defining the relation.

import { FilterableField, FilterableConnection, IDField } from '@ptc-org/nestjs-query-graphql'
import { ObjectType, ID, GraphQLISODateTime, Field } from '@nestjs/graphql'
import { SubTaskDTO } from '../../sub-task/dto/sub-task.dto'

@ObjectType('TodoItem')
@FilterableConnection('subTasks', () => SubTaskDTO, { enableAggregate: false })
export class TodoItemDTO {
@IDField(() => ID)
id!: number

@FilterableField()
title!: string

@FilterableField({ nullable: true })
description?: string

@FilterableField()
completed!: boolean

@FilterableField(() => GraphQLISODateTime)
created!: Date

@FilterableField(() => GraphQLISODateTime)
updated!: Date

@Field()
age!: number

@FilterableField()
priority!: number
}

Group aggregate on DAY/WEEK/MONTH

caution

This functionality only works for typeorm!

You can change how datetime fields are grouped by passing WEEK, MONTH or YEAR to the date fields by.

{
todoItemAggregate {
groupBy {
created(by: MONTH)
}
count {
id
}
sum {
id
}
}
}