Skip to content

Add support for aggregate functions #589

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 54 commits into from
May 8, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
bc29fd3
Add support for aggregates
spitfire55 May 2, 2025
a11a31e
Add totalCount back
spitfire55 May 2, 2025
4ac585c
Add aggregate sql test
spitfire55 May 2, 2025
8f5576e
Add totalCount case to transpile to_sql. Remove aggregate out
spitfire55 May 2, 2025
f5dec6a
Add aggregate types. Fix totalCounts directive
spitfire55 May 2, 2025
8db2ae8
Fix aggregate test
spitfire55 May 2, 2025
ff33b2e
Add expected output for aggregate test. Start addressing regressions …
spitfire55 May 2, 2025
49034ad
Revert this commit later...
spitfire55 May 3, 2025
8e50414
Formatting
spitfire55 May 3, 2025
eea10d3
Fix function relation test
spitfire55 May 3, 2025
1adb32a
Fix more tests
spitfire55 May 3, 2025
e939ff6
Add new aggregate types to inflection_types expected
spitfire55 May 3, 2025
5549417
Revert ordering of edges and pageInfo. Update omit_exotic_types expec…
spitfire55 May 3, 2025
8556685
Update override_type_name expected output with new aggregate types
spitfire55 May 3, 2025
9a33da2
Update resolve___schema expected out
spitfire55 May 3, 2025
173ce67
Fix non-null edge type and remove description for graphql schema cons…
spitfire55 May 3, 2025
b5069de
Updated expected test output for resolve_graphiql_schema
spitfire55 May 3, 2025
3497c60
Exclude UUIDs from min/max aggregation
spitfire55 May 3, 2025
d9b000a
Add docs
spitfire55 May 3, 2025
2015b60
Add changelog
spitfire55 May 3, 2025
3c02ab0
Remove dumb comments
spitfire55 May 3, 2025
84ae4aa
Add more tests for aggregate
spitfire55 May 3, 2025
247e4e4
Revert changes to installcheck
spitfire55 May 3, 2025
07d53b5
Remove pagination test (for now)
spitfire55 May 3, 2025
3f70ecc
Update api.md
imor May 6, 2025
e7846af
Update aggregate.sql
imor May 6, 2025
125a4d0
Update transpile.rs
imor May 6, 2025
7f4a867
remove repetitive information in docs
imor May 6, 2025
bb3d485
add rollback at the end of aggregate.sql test file
imor May 6, 2025
deaa386
rename aggregate_selection to aggregate_builder
imor May 6, 2025
28b69c2
rename has_numeric to has_sum_avgable
imor May 6, 2025
db152a7
remove commented out code
imor May 6, 2025
18e0402
use lowercase sql
imor May 6, 2025
e7ff9e2
Minor changes and fixes
spitfire55 May 6, 2025
0e01129
Helper methods for AggregateOperation
spitfire55 May 6, 2025
5dbeedc
- Create Aggregate variant in ConnnectionSelection
spitfire55 May 6, 2025
75f68d6
- Add support for fragment spreads and inline fragments to aggregates
spitfire55 May 6, 2025
d2a5869
Remove unused variable_definitions args
spitfire55 May 6, 2025
35d83d3
keep using the old method of pushing to builder_fields
imor May 7, 2025
3fdaa4e
remove special check for duplicate aggregate field
imor May 7, 2025
047215e
use Arc::clone on column instead of on Option<Column>
imor May 7, 2025
d53e383
rename a function
imor May 7, 2025
7785bb7
rename selections to column_builders
imor May 7, 2025
44bc094
simplify nested match statements
imor May 7, 2025
2ddbce6
remove a comment
imor May 7, 2025
303550e
Merge branch 'master' into spitfire55_master
imor May 7, 2025
401782f
fix a couple of clippy warnings
imor May 7, 2025
57ff018
remove a comment
imor May 7, 2025
9ac5361
return Option<String> instead of empty strings to represent missing v…
imor May 7, 2025
5b28d73
add a test for aliases in aggregates
imor May 7, 2025
401fd6f
Put aggregates behind comment directive
spitfire55 May 7, 2025
8444858
Fix tests
spitfire55 May 7, 2025
efb7ca4
fix pre-commit hook errors
imor May 8, 2025
c021f40
improve an error message
imor May 8, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions bin/installcheck
Original file line number Diff line number Diff line change
Expand Up @@ -35,17 +35,16 @@ createdb contrib_regression
# Tests #
#########
TESTDIR="test"
PGXS=$(dirname `pg_config --pgxs`)
PGXS=$(dirname $(pg_config --pgxs))
REGRESS="${PGXS}/../test/regress/pg_regress"

# Test names can be passed as parameters to this script.
# If any test names are passed run only those tests.
# Otherwise run all tests.
if [ "$#" -ne 0 ]
then
TESTS=$@
if [ "$#" -ne 0 ]; then
TESTS=$@
else
TESTS=$(ls ${TESTDIR}/sql | sed -e 's/\..*$//' | sort )
TESTS=$(ls ${TESTDIR}/sql | sed -e 's/\..*$//' | sort)
fi

# Execute the test fixtures
Expand Down
172 changes: 171 additions & 1 deletion docs/api.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

In our API, each SQL table is reflected as a set of GraphQL types. At a high level, tables become types and columns/foreign keys become fields on those types.


Expand Down Expand Up @@ -167,6 +166,9 @@ Connections wrap a result set with some additional metadata.
# Result set
edges: [BlogEdge!]!

# Aggregate functions
aggregate: BlogAggregate

}
```

Expand Down Expand Up @@ -264,8 +266,176 @@ Connections wrap a result set with some additional metadata.

The `totalCount` field is disabled by default because it can be expensive on large tables. To enable it use a [comment directive](configuration.md#totalcount)

#### Aggregates

Aggregate functions are available on the collection's `aggregate` field when enabled via [comment directive](configuration.md#aggregate). These allow you to perform calculations on the collection of records that match your filter criteria.

The supported aggregate operations are:

- **count**: Always available, returns the number of records matching the query
- **sum**: Available for numeric fields, returns the sum of values
- **avg**: Available for numeric fields, returns the average (mean) of values
- **min**: Available for numeric, string, boolean, and date/time fields, returns the minimum value
- **max**: Available for numeric, string, boolean, and date/time fields, returns the maximum value

**Example**

=== "Query"

```graphql
{
blogCollection(
filter: { rating: { gt: 3 } }
) {
aggregate {
count
sum {
rating
visits
}
avg {
rating
}
min {
createdAt
title
}
max {
rating
updatedAt
}
}
}
}
```

=== "Response"

```json
{
"data": {
"blogCollection": {
"aggregate": {
"count": 5,
"sum": {
"rating": 23,
"visits": 1250
},
"avg": {
"rating": 4.6
},
"min": {
"createdAt": "2022-01-15T08:30:00Z",
"title": "A Blog Post"
},
"max": {
"rating": 5,
"updatedAt": "2023-04-22T14:15:30Z"
}
}
}
}
}
```

**GraphQL Types**
=== "BlogAggregate"

```graphql
"""Aggregate results for `Blog`"""
type BlogAggregate {
"""The number of records matching the query"""
count: Int!

"""Summation aggregates for `Blog`"""
sum: BlogSumAggregateResult

"""Average aggregates for `Blog`"""
avg: BlogAvgAggregateResult

"""Minimum aggregates for comparable fields"""
min: BlogMinAggregateResult

"""Maximum aggregates for comparable fields"""
max: BlogMaxAggregateResult
}
```

=== "BlogSumAggregateResult"

```graphql
"""Result of summation aggregation for `Blog`"""
type BlogSumAggregateResult {
"""Sum of rating values"""
rating: BigFloat

"""Sum of visits values"""
visits: BigInt

# Other numeric fields...
}
```

=== "BlogAvgAggregateResult"

```graphql
"""Result of average aggregation for `Blog`"""
type BlogAvgAggregateResult {
"""Average of rating values"""
rating: BigFloat

"""Average of visits values"""
visits: BigFloat

# Other numeric fields...
}
```

=== "BlogMinAggregateResult"

```graphql
"""Result of minimum aggregation for `Blog`"""
type BlogMinAggregateResult {
"""Minimum rating value"""
rating: Float

"""Minimum title value"""
title: String

"""Minimum createdAt value"""
createdAt: Datetime

# Other comparable fields...
}
```

=== "BlogMaxAggregateResult"

```graphql
"""Result of maximum aggregation for `Blog`"""
type BlogMaxAggregateResult {
"""Maximum rating value"""
rating: Float

"""Maximum title value"""
title: String

"""Maximum updatedAt value"""
updatedAt: Datetime

# Other comparable fields...
}
```

!!! note

- The return type for `sum` depends on the input type: integer fields return `BigInt`, while other numeric fields return `BigFloat`.
- The return type for `avg` is always `BigFloat`.
- The return types for `min` and `max` match the original field types.

!!! note

The `aggregate` field is disabled by default because it can be expensive on large tables. To enable it use a [comment directive](configuration.md#Aggregate)

#### Pagination

Expand Down
1 change: 1 addition & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,3 +95,4 @@
- bugfix: qualify schema refs

## master
- feature: Add support for aggregate functions (count, sum, avg, min, max) on collection types
35 changes: 35 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,41 @@ create table "BlogPost"(
comment on table "BlogPost" is e'@graphql({"totalCount": {"enabled": true}})';
```

### Aggregate

The `aggregate` field is an opt-in field that extends a table's Connection type. It provides various aggregate functions like count, sum, avg, min, and max that operate on the collection of records that match the query's filters.

```graphql
type BlogPostConnection {
edges: [BlogPostEdge!]!
pageInfo: PageInfo!

"""Aggregate functions calculated on the collection of `BlogPost`"""
aggregate: BlogPostAggregate # this field
}
```

To enable the `aggregate` field for a table, use the directive:

```sql
comment on table "BlogPost" is e'@graphql({"aggregate": {"enabled": true}})';
```

For example:
```sql
create table "BlogPost"(
id serial primary key,
title varchar(255) not null,
rating int not null
);
comment on table "BlogPost" is e'@graphql({"aggregate": {"enabled": true}})';
```

You can combine both totalCount and aggregate directives:

```sql
comment on table "BlogPost" is e'@graphql({"totalCount": {"enabled": true}, "aggregate": {"enabled": true}})';
```

### Renaming

Expand Down
8 changes: 8 additions & 0 deletions sql/load_sql_context.sql
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,14 @@ select
false
)
),
'aggregate', jsonb_build_object(
'enabled', coalesce(
(
d.directive -> 'aggregate' ->> 'enabled' = 'true'
),
false
)
),
'primary_key_columns', d.directive -> 'primary_key_columns',
'foreign_keys', d.directive -> 'foreign_keys'
)
Expand Down
Loading