From 13026fcbbf6b698dc2a7c77ff90bcf5cf061b5c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Isak=20Stenstr=C3=B6m?= Date: Thu, 26 Mar 2026 12:01:43 +0100 Subject: [PATCH] Don't count all rows when using pagination Ent counts all rows in the table when using pagination, even when it doesn't need to. --- ent/README.md | 11 +- ent/entc.go | 9 + ent/gen/ent/gql_pagination.go | 520 +++++++++++++++++----------------- ent/template/pagination.tmpl | 77 +++++ go.mod | 2 + 5 files changed, 357 insertions(+), 262 deletions(-) create mode 100644 ent/template/pagination.tmpl diff --git a/ent/README.md b/ent/README.md index e7e83e13..ab0cd047 100644 --- a/ent/README.md +++ b/ent/README.md @@ -1,4 +1,6 @@ -# Ent models +# Ent + +## Schema This directory contains the Ent schema definitions used by the application. @@ -16,7 +18,7 @@ Why the two-step process 2. Generate files from `ent/schema` without privacy policies. 3. Generate files from `ent/authschema` to create the final files. -## Adding a new schema +### Adding a new schema To add a new schema: @@ -34,3 +36,8 @@ To add a new schema: 4. Add tests for the privacy policies in `ent/authschema/privacy_test.go`. 5. Run `./tools/update_schema.sh` to regenerate the code with the new schema and policies. + +## Templates + +Templates used by Ent live in `ent/template`. These are used to change the code +that Ent generates. When updating Ent, these probably need to be updated. diff --git a/ent/entc.go b/ent/entc.go index b932a70c..e5974b43 100644 --- a/ent/entc.go +++ b/ent/entc.go @@ -12,8 +12,17 @@ import ( "github.com/hedwigz/entviz" ) +func getGqlTemplates() []*gen.Template { + customPaginationTemplate := gen.MustParse(gen.NewTemplate("./ent/template/pagination.tmpl"). + Funcs(entgql.TemplateFuncs). + ParseFiles("./ent/template/pagination.tmpl"), + ) + return append(entgql.AllTemplates, customPaginationTemplate) +} + func main() { ex, err := entgql.NewExtension( + entgql.WithTemplates(getGqlTemplates()...), entgql.WithSchemaGenerator(), entgql.WithSchemaPath("./internal/graphql/schema/ent.graphql"), entgql.WithWhereInputs(true), diff --git a/ent/gen/ent/gql_pagination.go b/ent/gen/ent/gql_pagination.go index cf32bf86..f6906ddb 100644 --- a/ent/gen/ent/gql_pagination.go +++ b/ent/gen/ent/gql_pagination.go @@ -295,17 +295,17 @@ func (a *ActionQuery) Paginate( } conn := &ActionConnection{Edges: []*ActionEdge{}} ignoredEdges := !hasCollectedField(ctx, edgesField) - if hasCollectedField(ctx, totalCountField) || hasCollectedField(ctx, pageInfoField) { - hasPagination := after != nil || first != nil || before != nil || last != nil - if hasPagination || ignoredEdges { - c := a.Clone() - c.ctx.Fields = nil - if conn.TotalCount, err = c.Count(ctx); err != nil { - return nil, err - } - conn.PageInfo.HasNextPage = first != nil && conn.TotalCount > 0 - conn.PageInfo.HasPreviousPage = last != nil && conn.TotalCount > 0 + needTotalCount := hasCollectedField(ctx, totalCountField) + needPageInfo := hasCollectedField(ctx, pageInfoField) + hasPagination := after != nil || first != nil || before != nil || last != nil + if (needTotalCount && hasPagination) || (ignoredEdges && (needTotalCount || needPageInfo)) { + c := a.Clone() + c.ctx.Fields = nil + if conn.TotalCount, err = c.Count(ctx); err != nil { + return nil, err } + conn.PageInfo.HasNextPage = first != nil && conn.TotalCount > 0 + conn.PageInfo.HasPreviousPage = last != nil && conn.TotalCount > 0 } if ignoredEdges || (first != nil && *first == 0) || (last != nil && *last == 0) { return conn, nil @@ -544,17 +544,17 @@ func (acs *ActionCacheStatisticsQuery) Paginate( } conn := &ActionCacheStatisticsConnection{Edges: []*ActionCacheStatisticsEdge{}} ignoredEdges := !hasCollectedField(ctx, edgesField) - if hasCollectedField(ctx, totalCountField) || hasCollectedField(ctx, pageInfoField) { - hasPagination := after != nil || first != nil || before != nil || last != nil - if hasPagination || ignoredEdges { - c := acs.Clone() - c.ctx.Fields = nil - if conn.TotalCount, err = c.Count(ctx); err != nil { - return nil, err - } - conn.PageInfo.HasNextPage = first != nil && conn.TotalCount > 0 - conn.PageInfo.HasPreviousPage = last != nil && conn.TotalCount > 0 + needTotalCount := hasCollectedField(ctx, totalCountField) + needPageInfo := hasCollectedField(ctx, pageInfoField) + hasPagination := after != nil || first != nil || before != nil || last != nil + if (needTotalCount && hasPagination) || (ignoredEdges && (needTotalCount || needPageInfo)) { + c := acs.Clone() + c.ctx.Fields = nil + if conn.TotalCount, err = c.Count(ctx); err != nil { + return nil, err } + conn.PageInfo.HasNextPage = first != nil && conn.TotalCount > 0 + conn.PageInfo.HasPreviousPage = last != nil && conn.TotalCount > 0 } if ignoredEdges || (first != nil && *first == 0) || (last != nil && *last == 0) { return conn, nil @@ -793,17 +793,17 @@ func (ad *ActionDataQuery) Paginate( } conn := &ActionDataConnection{Edges: []*ActionDataEdge{}} ignoredEdges := !hasCollectedField(ctx, edgesField) - if hasCollectedField(ctx, totalCountField) || hasCollectedField(ctx, pageInfoField) { - hasPagination := after != nil || first != nil || before != nil || last != nil - if hasPagination || ignoredEdges { - c := ad.Clone() - c.ctx.Fields = nil - if conn.TotalCount, err = c.Count(ctx); err != nil { - return nil, err - } - conn.PageInfo.HasNextPage = first != nil && conn.TotalCount > 0 - conn.PageInfo.HasPreviousPage = last != nil && conn.TotalCount > 0 + needTotalCount := hasCollectedField(ctx, totalCountField) + needPageInfo := hasCollectedField(ctx, pageInfoField) + hasPagination := after != nil || first != nil || before != nil || last != nil + if (needTotalCount && hasPagination) || (ignoredEdges && (needTotalCount || needPageInfo)) { + c := ad.Clone() + c.ctx.Fields = nil + if conn.TotalCount, err = c.Count(ctx); err != nil { + return nil, err } + conn.PageInfo.HasNextPage = first != nil && conn.TotalCount > 0 + conn.PageInfo.HasPreviousPage = last != nil && conn.TotalCount > 0 } if ignoredEdges || (first != nil && *first == 0) || (last != nil && *last == 0) { return conn, nil @@ -1042,17 +1042,17 @@ func (as *ActionSummaryQuery) Paginate( } conn := &ActionSummaryConnection{Edges: []*ActionSummaryEdge{}} ignoredEdges := !hasCollectedField(ctx, edgesField) - if hasCollectedField(ctx, totalCountField) || hasCollectedField(ctx, pageInfoField) { - hasPagination := after != nil || first != nil || before != nil || last != nil - if hasPagination || ignoredEdges { - c := as.Clone() - c.ctx.Fields = nil - if conn.TotalCount, err = c.Count(ctx); err != nil { - return nil, err - } - conn.PageInfo.HasNextPage = first != nil && conn.TotalCount > 0 - conn.PageInfo.HasPreviousPage = last != nil && conn.TotalCount > 0 + needTotalCount := hasCollectedField(ctx, totalCountField) + needPageInfo := hasCollectedField(ctx, pageInfoField) + hasPagination := after != nil || first != nil || before != nil || last != nil + if (needTotalCount && hasPagination) || (ignoredEdges && (needTotalCount || needPageInfo)) { + c := as.Clone() + c.ctx.Fields = nil + if conn.TotalCount, err = c.Count(ctx); err != nil { + return nil, err } + conn.PageInfo.HasNextPage = first != nil && conn.TotalCount > 0 + conn.PageInfo.HasPreviousPage = last != nil && conn.TotalCount > 0 } if ignoredEdges || (first != nil && *first == 0) || (last != nil && *last == 0) { return conn, nil @@ -1291,17 +1291,17 @@ func (am *ArtifactMetricsQuery) Paginate( } conn := &ArtifactMetricsConnection{Edges: []*ArtifactMetricsEdge{}} ignoredEdges := !hasCollectedField(ctx, edgesField) - if hasCollectedField(ctx, totalCountField) || hasCollectedField(ctx, pageInfoField) { - hasPagination := after != nil || first != nil || before != nil || last != nil - if hasPagination || ignoredEdges { - c := am.Clone() - c.ctx.Fields = nil - if conn.TotalCount, err = c.Count(ctx); err != nil { - return nil, err - } - conn.PageInfo.HasNextPage = first != nil && conn.TotalCount > 0 - conn.PageInfo.HasPreviousPage = last != nil && conn.TotalCount > 0 + needTotalCount := hasCollectedField(ctx, totalCountField) + needPageInfo := hasCollectedField(ctx, pageInfoField) + hasPagination := after != nil || first != nil || before != nil || last != nil + if (needTotalCount && hasPagination) || (ignoredEdges && (needTotalCount || needPageInfo)) { + c := am.Clone() + c.ctx.Fields = nil + if conn.TotalCount, err = c.Count(ctx); err != nil { + return nil, err } + conn.PageInfo.HasNextPage = first != nil && conn.TotalCount > 0 + conn.PageInfo.HasPreviousPage = last != nil && conn.TotalCount > 0 } if ignoredEdges || (first != nil && *first == 0) || (last != nil && *last == 0) { return conn, nil @@ -1540,17 +1540,17 @@ func (au *AuthenticatedUserQuery) Paginate( } conn := &AuthenticatedUserConnection{Edges: []*AuthenticatedUserEdge{}} ignoredEdges := !hasCollectedField(ctx, edgesField) - if hasCollectedField(ctx, totalCountField) || hasCollectedField(ctx, pageInfoField) { - hasPagination := after != nil || first != nil || before != nil || last != nil - if hasPagination || ignoredEdges { - c := au.Clone() - c.ctx.Fields = nil - if conn.TotalCount, err = c.Count(ctx); err != nil { - return nil, err - } - conn.PageInfo.HasNextPage = first != nil && conn.TotalCount > 0 - conn.PageInfo.HasPreviousPage = last != nil && conn.TotalCount > 0 + needTotalCount := hasCollectedField(ctx, totalCountField) + needPageInfo := hasCollectedField(ctx, pageInfoField) + hasPagination := after != nil || first != nil || before != nil || last != nil + if (needTotalCount && hasPagination) || (ignoredEdges && (needTotalCount || needPageInfo)) { + c := au.Clone() + c.ctx.Fields = nil + if conn.TotalCount, err = c.Count(ctx); err != nil { + return nil, err } + conn.PageInfo.HasNextPage = first != nil && conn.TotalCount > 0 + conn.PageInfo.HasPreviousPage = last != nil && conn.TotalCount > 0 } if ignoredEdges || (first != nil && *first == 0) || (last != nil && *last == 0) { return conn, nil @@ -1789,17 +1789,17 @@ func (bi *BazelInvocationQuery) Paginate( } conn := &BazelInvocationConnection{Edges: []*BazelInvocationEdge{}} ignoredEdges := !hasCollectedField(ctx, edgesField) - if hasCollectedField(ctx, totalCountField) || hasCollectedField(ctx, pageInfoField) { - hasPagination := after != nil || first != nil || before != nil || last != nil - if hasPagination || ignoredEdges { - c := bi.Clone() - c.ctx.Fields = nil - if conn.TotalCount, err = c.Count(ctx); err != nil { - return nil, err - } - conn.PageInfo.HasNextPage = first != nil && conn.TotalCount > 0 - conn.PageInfo.HasPreviousPage = last != nil && conn.TotalCount > 0 + needTotalCount := hasCollectedField(ctx, totalCountField) + needPageInfo := hasCollectedField(ctx, pageInfoField) + hasPagination := after != nil || first != nil || before != nil || last != nil + if (needTotalCount && hasPagination) || (ignoredEdges && (needTotalCount || needPageInfo)) { + c := bi.Clone() + c.ctx.Fields = nil + if conn.TotalCount, err = c.Count(ctx); err != nil { + return nil, err } + conn.PageInfo.HasNextPage = first != nil && conn.TotalCount > 0 + conn.PageInfo.HasPreviousPage = last != nil && conn.TotalCount > 0 } if ignoredEdges || (first != nil && *first == 0) || (last != nil && *last == 0) { return conn, nil @@ -2103,17 +2103,17 @@ func (b *BuildQuery) Paginate( } conn := &BuildConnection{Edges: []*BuildEdge{}} ignoredEdges := !hasCollectedField(ctx, edgesField) - if hasCollectedField(ctx, totalCountField) || hasCollectedField(ctx, pageInfoField) { - hasPagination := after != nil || first != nil || before != nil || last != nil - if hasPagination || ignoredEdges { - c := b.Clone() - c.ctx.Fields = nil - if conn.TotalCount, err = c.Count(ctx); err != nil { - return nil, err - } - conn.PageInfo.HasNextPage = first != nil && conn.TotalCount > 0 - conn.PageInfo.HasPreviousPage = last != nil && conn.TotalCount > 0 + needTotalCount := hasCollectedField(ctx, totalCountField) + needPageInfo := hasCollectedField(ctx, pageInfoField) + hasPagination := after != nil || first != nil || before != nil || last != nil + if (needTotalCount && hasPagination) || (ignoredEdges && (needTotalCount || needPageInfo)) { + c := b.Clone() + c.ctx.Fields = nil + if conn.TotalCount, err = c.Count(ctx); err != nil { + return nil, err } + conn.PageInfo.HasNextPage = first != nil && conn.TotalCount > 0 + conn.PageInfo.HasPreviousPage = last != nil && conn.TotalCount > 0 } if ignoredEdges || (first != nil && *first == 0) || (last != nil && *last == 0) { return conn, nil @@ -2399,17 +2399,17 @@ func (bgm *BuildGraphMetricsQuery) Paginate( } conn := &BuildGraphMetricsConnection{Edges: []*BuildGraphMetricsEdge{}} ignoredEdges := !hasCollectedField(ctx, edgesField) - if hasCollectedField(ctx, totalCountField) || hasCollectedField(ctx, pageInfoField) { - hasPagination := after != nil || first != nil || before != nil || last != nil - if hasPagination || ignoredEdges { - c := bgm.Clone() - c.ctx.Fields = nil - if conn.TotalCount, err = c.Count(ctx); err != nil { - return nil, err - } - conn.PageInfo.HasNextPage = first != nil && conn.TotalCount > 0 - conn.PageInfo.HasPreviousPage = last != nil && conn.TotalCount > 0 + needTotalCount := hasCollectedField(ctx, totalCountField) + needPageInfo := hasCollectedField(ctx, pageInfoField) + hasPagination := after != nil || first != nil || before != nil || last != nil + if (needTotalCount && hasPagination) || (ignoredEdges && (needTotalCount || needPageInfo)) { + c := bgm.Clone() + c.ctx.Fields = nil + if conn.TotalCount, err = c.Count(ctx); err != nil { + return nil, err } + conn.PageInfo.HasNextPage = first != nil && conn.TotalCount > 0 + conn.PageInfo.HasPreviousPage = last != nil && conn.TotalCount > 0 } if ignoredEdges || (first != nil && *first == 0) || (last != nil && *last == 0) { return conn, nil @@ -2648,17 +2648,17 @@ func (c *ConfigurationQuery) Paginate( } conn := &ConfigurationConnection{Edges: []*ConfigurationEdge{}} ignoredEdges := !hasCollectedField(ctx, edgesField) - if hasCollectedField(ctx, totalCountField) || hasCollectedField(ctx, pageInfoField) { - hasPagination := after != nil || first != nil || before != nil || last != nil - if hasPagination || ignoredEdges { - c := c.Clone() - c.ctx.Fields = nil - if conn.TotalCount, err = c.Count(ctx); err != nil { - return nil, err - } - conn.PageInfo.HasNextPage = first != nil && conn.TotalCount > 0 - conn.PageInfo.HasPreviousPage = last != nil && conn.TotalCount > 0 + needTotalCount := hasCollectedField(ctx, totalCountField) + needPageInfo := hasCollectedField(ctx, pageInfoField) + hasPagination := after != nil || first != nil || before != nil || last != nil + if (needTotalCount && hasPagination) || (ignoredEdges && (needTotalCount || needPageInfo)) { + c := c.Clone() + c.ctx.Fields = nil + if conn.TotalCount, err = c.Count(ctx); err != nil { + return nil, err } + conn.PageInfo.HasNextPage = first != nil && conn.TotalCount > 0 + conn.PageInfo.HasPreviousPage = last != nil && conn.TotalCount > 0 } if ignoredEdges || (first != nil && *first == 0) || (last != nil && *last == 0) { return conn, nil @@ -2897,17 +2897,17 @@ func (cm *ConnectionMetadataQuery) Paginate( } conn := &ConnectionMetadataConnection{Edges: []*ConnectionMetadataEdge{}} ignoredEdges := !hasCollectedField(ctx, edgesField) - if hasCollectedField(ctx, totalCountField) || hasCollectedField(ctx, pageInfoField) { - hasPagination := after != nil || first != nil || before != nil || last != nil - if hasPagination || ignoredEdges { - c := cm.Clone() - c.ctx.Fields = nil - if conn.TotalCount, err = c.Count(ctx); err != nil { - return nil, err - } - conn.PageInfo.HasNextPage = first != nil && conn.TotalCount > 0 - conn.PageInfo.HasPreviousPage = last != nil && conn.TotalCount > 0 + needTotalCount := hasCollectedField(ctx, totalCountField) + needPageInfo := hasCollectedField(ctx, pageInfoField) + hasPagination := after != nil || first != nil || before != nil || last != nil + if (needTotalCount && hasPagination) || (ignoredEdges && (needTotalCount || needPageInfo)) { + c := cm.Clone() + c.ctx.Fields = nil + if conn.TotalCount, err = c.Count(ctx); err != nil { + return nil, err } + conn.PageInfo.HasNextPage = first != nil && conn.TotalCount > 0 + conn.PageInfo.HasPreviousPage = last != nil && conn.TotalCount > 0 } if ignoredEdges || (first != nil && *first == 0) || (last != nil && *last == 0) { return conn, nil @@ -3146,17 +3146,17 @@ func (gm *GarbageMetricsQuery) Paginate( } conn := &GarbageMetricsConnection{Edges: []*GarbageMetricsEdge{}} ignoredEdges := !hasCollectedField(ctx, edgesField) - if hasCollectedField(ctx, totalCountField) || hasCollectedField(ctx, pageInfoField) { - hasPagination := after != nil || first != nil || before != nil || last != nil - if hasPagination || ignoredEdges { - c := gm.Clone() - c.ctx.Fields = nil - if conn.TotalCount, err = c.Count(ctx); err != nil { - return nil, err - } - conn.PageInfo.HasNextPage = first != nil && conn.TotalCount > 0 - conn.PageInfo.HasPreviousPage = last != nil && conn.TotalCount > 0 + needTotalCount := hasCollectedField(ctx, totalCountField) + needPageInfo := hasCollectedField(ctx, pageInfoField) + hasPagination := after != nil || first != nil || before != nil || last != nil + if (needTotalCount && hasPagination) || (ignoredEdges && (needTotalCount || needPageInfo)) { + c := gm.Clone() + c.ctx.Fields = nil + if conn.TotalCount, err = c.Count(ctx); err != nil { + return nil, err } + conn.PageInfo.HasNextPage = first != nil && conn.TotalCount > 0 + conn.PageInfo.HasPreviousPage = last != nil && conn.TotalCount > 0 } if ignoredEdges || (first != nil && *first == 0) || (last != nil && *last == 0) { return conn, nil @@ -3395,17 +3395,17 @@ func (in *InstanceNameQuery) Paginate( } conn := &InstanceNameConnection{Edges: []*InstanceNameEdge{}} ignoredEdges := !hasCollectedField(ctx, edgesField) - if hasCollectedField(ctx, totalCountField) || hasCollectedField(ctx, pageInfoField) { - hasPagination := after != nil || first != nil || before != nil || last != nil - if hasPagination || ignoredEdges { - c := in.Clone() - c.ctx.Fields = nil - if conn.TotalCount, err = c.Count(ctx); err != nil { - return nil, err - } - conn.PageInfo.HasNextPage = first != nil && conn.TotalCount > 0 - conn.PageInfo.HasPreviousPage = last != nil && conn.TotalCount > 0 + needTotalCount := hasCollectedField(ctx, totalCountField) + needPageInfo := hasCollectedField(ctx, pageInfoField) + hasPagination := after != nil || first != nil || before != nil || last != nil + if (needTotalCount && hasPagination) || (ignoredEdges && (needTotalCount || needPageInfo)) { + c := in.Clone() + c.ctx.Fields = nil + if conn.TotalCount, err = c.Count(ctx); err != nil { + return nil, err } + conn.PageInfo.HasNextPage = first != nil && conn.TotalCount > 0 + conn.PageInfo.HasPreviousPage = last != nil && conn.TotalCount > 0 } if ignoredEdges || (first != nil && *first == 0) || (last != nil && *last == 0) { return conn, nil @@ -3644,17 +3644,17 @@ func (it *InvocationTargetQuery) Paginate( } conn := &InvocationTargetConnection{Edges: []*InvocationTargetEdge{}} ignoredEdges := !hasCollectedField(ctx, edgesField) - if hasCollectedField(ctx, totalCountField) || hasCollectedField(ctx, pageInfoField) { - hasPagination := after != nil || first != nil || before != nil || last != nil - if hasPagination || ignoredEdges { - c := it.Clone() - c.ctx.Fields = nil - if conn.TotalCount, err = c.Count(ctx); err != nil { - return nil, err - } - conn.PageInfo.HasNextPage = first != nil && conn.TotalCount > 0 - conn.PageInfo.HasPreviousPage = last != nil && conn.TotalCount > 0 + needTotalCount := hasCollectedField(ctx, totalCountField) + needPageInfo := hasCollectedField(ctx, pageInfoField) + hasPagination := after != nil || first != nil || before != nil || last != nil + if (needTotalCount && hasPagination) || (ignoredEdges && (needTotalCount || needPageInfo)) { + c := it.Clone() + c.ctx.Fields = nil + if conn.TotalCount, err = c.Count(ctx); err != nil { + return nil, err } + conn.PageInfo.HasNextPage = first != nil && conn.TotalCount > 0 + conn.PageInfo.HasPreviousPage = last != nil && conn.TotalCount > 0 } if ignoredEdges || (first != nil && *first == 0) || (last != nil && *last == 0) { return conn, nil @@ -3958,17 +3958,17 @@ func (mm *MemoryMetricsQuery) Paginate( } conn := &MemoryMetricsConnection{Edges: []*MemoryMetricsEdge{}} ignoredEdges := !hasCollectedField(ctx, edgesField) - if hasCollectedField(ctx, totalCountField) || hasCollectedField(ctx, pageInfoField) { - hasPagination := after != nil || first != nil || before != nil || last != nil - if hasPagination || ignoredEdges { - c := mm.Clone() - c.ctx.Fields = nil - if conn.TotalCount, err = c.Count(ctx); err != nil { - return nil, err - } - conn.PageInfo.HasNextPage = first != nil && conn.TotalCount > 0 - conn.PageInfo.HasPreviousPage = last != nil && conn.TotalCount > 0 + needTotalCount := hasCollectedField(ctx, totalCountField) + needPageInfo := hasCollectedField(ctx, pageInfoField) + hasPagination := after != nil || first != nil || before != nil || last != nil + if (needTotalCount && hasPagination) || (ignoredEdges && (needTotalCount || needPageInfo)) { + c := mm.Clone() + c.ctx.Fields = nil + if conn.TotalCount, err = c.Count(ctx); err != nil { + return nil, err } + conn.PageInfo.HasNextPage = first != nil && conn.TotalCount > 0 + conn.PageInfo.HasPreviousPage = last != nil && conn.TotalCount > 0 } if ignoredEdges || (first != nil && *first == 0) || (last != nil && *last == 0) { return conn, nil @@ -4207,17 +4207,17 @@ func (m *MetricsQuery) Paginate( } conn := &MetricsConnection{Edges: []*MetricsEdge{}} ignoredEdges := !hasCollectedField(ctx, edgesField) - if hasCollectedField(ctx, totalCountField) || hasCollectedField(ctx, pageInfoField) { - hasPagination := after != nil || first != nil || before != nil || last != nil - if hasPagination || ignoredEdges { - c := m.Clone() - c.ctx.Fields = nil - if conn.TotalCount, err = c.Count(ctx); err != nil { - return nil, err - } - conn.PageInfo.HasNextPage = first != nil && conn.TotalCount > 0 - conn.PageInfo.HasPreviousPage = last != nil && conn.TotalCount > 0 + needTotalCount := hasCollectedField(ctx, totalCountField) + needPageInfo := hasCollectedField(ctx, pageInfoField) + hasPagination := after != nil || first != nil || before != nil || last != nil + if (needTotalCount && hasPagination) || (ignoredEdges && (needTotalCount || needPageInfo)) { + c := m.Clone() + c.ctx.Fields = nil + if conn.TotalCount, err = c.Count(ctx); err != nil { + return nil, err } + conn.PageInfo.HasNextPage = first != nil && conn.TotalCount > 0 + conn.PageInfo.HasPreviousPage = last != nil && conn.TotalCount > 0 } if ignoredEdges || (first != nil && *first == 0) || (last != nil && *last == 0) { return conn, nil @@ -4456,17 +4456,17 @@ func (md *MissDetailQuery) Paginate( } conn := &MissDetailConnection{Edges: []*MissDetailEdge{}} ignoredEdges := !hasCollectedField(ctx, edgesField) - if hasCollectedField(ctx, totalCountField) || hasCollectedField(ctx, pageInfoField) { - hasPagination := after != nil || first != nil || before != nil || last != nil - if hasPagination || ignoredEdges { - c := md.Clone() - c.ctx.Fields = nil - if conn.TotalCount, err = c.Count(ctx); err != nil { - return nil, err - } - conn.PageInfo.HasNextPage = first != nil && conn.TotalCount > 0 - conn.PageInfo.HasPreviousPage = last != nil && conn.TotalCount > 0 + needTotalCount := hasCollectedField(ctx, totalCountField) + needPageInfo := hasCollectedField(ctx, pageInfoField) + hasPagination := after != nil || first != nil || before != nil || last != nil + if (needTotalCount && hasPagination) || (ignoredEdges && (needTotalCount || needPageInfo)) { + c := md.Clone() + c.ctx.Fields = nil + if conn.TotalCount, err = c.Count(ctx); err != nil { + return nil, err } + conn.PageInfo.HasNextPage = first != nil && conn.TotalCount > 0 + conn.PageInfo.HasPreviousPage = last != nil && conn.TotalCount > 0 } if ignoredEdges || (first != nil && *first == 0) || (last != nil && *last == 0) { return conn, nil @@ -4705,17 +4705,17 @@ func (nm *NetworkMetricsQuery) Paginate( } conn := &NetworkMetricsConnection{Edges: []*NetworkMetricsEdge{}} ignoredEdges := !hasCollectedField(ctx, edgesField) - if hasCollectedField(ctx, totalCountField) || hasCollectedField(ctx, pageInfoField) { - hasPagination := after != nil || first != nil || before != nil || last != nil - if hasPagination || ignoredEdges { - c := nm.Clone() - c.ctx.Fields = nil - if conn.TotalCount, err = c.Count(ctx); err != nil { - return nil, err - } - conn.PageInfo.HasNextPage = first != nil && conn.TotalCount > 0 - conn.PageInfo.HasPreviousPage = last != nil && conn.TotalCount > 0 + needTotalCount := hasCollectedField(ctx, totalCountField) + needPageInfo := hasCollectedField(ctx, pageInfoField) + hasPagination := after != nil || first != nil || before != nil || last != nil + if (needTotalCount && hasPagination) || (ignoredEdges && (needTotalCount || needPageInfo)) { + c := nm.Clone() + c.ctx.Fields = nil + if conn.TotalCount, err = c.Count(ctx); err != nil { + return nil, err } + conn.PageInfo.HasNextPage = first != nil && conn.TotalCount > 0 + conn.PageInfo.HasPreviousPage = last != nil && conn.TotalCount > 0 } if ignoredEdges || (first != nil && *first == 0) || (last != nil && *last == 0) { return conn, nil @@ -4954,17 +4954,17 @@ func (rc *RunnerCountQuery) Paginate( } conn := &RunnerCountConnection{Edges: []*RunnerCountEdge{}} ignoredEdges := !hasCollectedField(ctx, edgesField) - if hasCollectedField(ctx, totalCountField) || hasCollectedField(ctx, pageInfoField) { - hasPagination := after != nil || first != nil || before != nil || last != nil - if hasPagination || ignoredEdges { - c := rc.Clone() - c.ctx.Fields = nil - if conn.TotalCount, err = c.Count(ctx); err != nil { - return nil, err - } - conn.PageInfo.HasNextPage = first != nil && conn.TotalCount > 0 - conn.PageInfo.HasPreviousPage = last != nil && conn.TotalCount > 0 + needTotalCount := hasCollectedField(ctx, totalCountField) + needPageInfo := hasCollectedField(ctx, pageInfoField) + hasPagination := after != nil || first != nil || before != nil || last != nil + if (needTotalCount && hasPagination) || (ignoredEdges && (needTotalCount || needPageInfo)) { + c := rc.Clone() + c.ctx.Fields = nil + if conn.TotalCount, err = c.Count(ctx); err != nil { + return nil, err } + conn.PageInfo.HasNextPage = first != nil && conn.TotalCount > 0 + conn.PageInfo.HasPreviousPage = last != nil && conn.TotalCount > 0 } if ignoredEdges || (first != nil && *first == 0) || (last != nil && *last == 0) { return conn, nil @@ -5203,17 +5203,17 @@ func (sc *SourceControlQuery) Paginate( } conn := &SourceControlConnection{Edges: []*SourceControlEdge{}} ignoredEdges := !hasCollectedField(ctx, edgesField) - if hasCollectedField(ctx, totalCountField) || hasCollectedField(ctx, pageInfoField) { - hasPagination := after != nil || first != nil || before != nil || last != nil - if hasPagination || ignoredEdges { - c := sc.Clone() - c.ctx.Fields = nil - if conn.TotalCount, err = c.Count(ctx); err != nil { - return nil, err - } - conn.PageInfo.HasNextPage = first != nil && conn.TotalCount > 0 - conn.PageInfo.HasPreviousPage = last != nil && conn.TotalCount > 0 + needTotalCount := hasCollectedField(ctx, totalCountField) + needPageInfo := hasCollectedField(ctx, pageInfoField) + hasPagination := after != nil || first != nil || before != nil || last != nil + if (needTotalCount && hasPagination) || (ignoredEdges && (needTotalCount || needPageInfo)) { + c := sc.Clone() + c.ctx.Fields = nil + if conn.TotalCount, err = c.Count(ctx); err != nil { + return nil, err } + conn.PageInfo.HasNextPage = first != nil && conn.TotalCount > 0 + conn.PageInfo.HasPreviousPage = last != nil && conn.TotalCount > 0 } if ignoredEdges || (first != nil && *first == 0) || (last != nil && *last == 0) { return conn, nil @@ -5452,17 +5452,17 @@ func (sns *SystemNetworkStatsQuery) Paginate( } conn := &SystemNetworkStatsConnection{Edges: []*SystemNetworkStatsEdge{}} ignoredEdges := !hasCollectedField(ctx, edgesField) - if hasCollectedField(ctx, totalCountField) || hasCollectedField(ctx, pageInfoField) { - hasPagination := after != nil || first != nil || before != nil || last != nil - if hasPagination || ignoredEdges { - c := sns.Clone() - c.ctx.Fields = nil - if conn.TotalCount, err = c.Count(ctx); err != nil { - return nil, err - } - conn.PageInfo.HasNextPage = first != nil && conn.TotalCount > 0 - conn.PageInfo.HasPreviousPage = last != nil && conn.TotalCount > 0 + needTotalCount := hasCollectedField(ctx, totalCountField) + needPageInfo := hasCollectedField(ctx, pageInfoField) + hasPagination := after != nil || first != nil || before != nil || last != nil + if (needTotalCount && hasPagination) || (ignoredEdges && (needTotalCount || needPageInfo)) { + c := sns.Clone() + c.ctx.Fields = nil + if conn.TotalCount, err = c.Count(ctx); err != nil { + return nil, err } + conn.PageInfo.HasNextPage = first != nil && conn.TotalCount > 0 + conn.PageInfo.HasPreviousPage = last != nil && conn.TotalCount > 0 } if ignoredEdges || (first != nil && *first == 0) || (last != nil && *last == 0) { return conn, nil @@ -5701,17 +5701,17 @@ func (t *TargetQuery) Paginate( } conn := &TargetConnection{Edges: []*TargetEdge{}} ignoredEdges := !hasCollectedField(ctx, edgesField) - if hasCollectedField(ctx, totalCountField) || hasCollectedField(ctx, pageInfoField) { - hasPagination := after != nil || first != nil || before != nil || last != nil - if hasPagination || ignoredEdges { - c := t.Clone() - c.ctx.Fields = nil - if conn.TotalCount, err = c.Count(ctx); err != nil { - return nil, err - } - conn.PageInfo.HasNextPage = first != nil && conn.TotalCount > 0 - conn.PageInfo.HasPreviousPage = last != nil && conn.TotalCount > 0 + needTotalCount := hasCollectedField(ctx, totalCountField) + needPageInfo := hasCollectedField(ctx, pageInfoField) + hasPagination := after != nil || first != nil || before != nil || last != nil + if (needTotalCount && hasPagination) || (ignoredEdges && (needTotalCount || needPageInfo)) { + c := t.Clone() + c.ctx.Fields = nil + if conn.TotalCount, err = c.Count(ctx); err != nil { + return nil, err } + conn.PageInfo.HasNextPage = first != nil && conn.TotalCount > 0 + conn.PageInfo.HasPreviousPage = last != nil && conn.TotalCount > 0 } if ignoredEdges || (first != nil && *first == 0) || (last != nil && *last == 0) { return conn, nil @@ -5950,17 +5950,17 @@ func (tm *TargetMetricsQuery) Paginate( } conn := &TargetMetricsConnection{Edges: []*TargetMetricsEdge{}} ignoredEdges := !hasCollectedField(ctx, edgesField) - if hasCollectedField(ctx, totalCountField) || hasCollectedField(ctx, pageInfoField) { - hasPagination := after != nil || first != nil || before != nil || last != nil - if hasPagination || ignoredEdges { - c := tm.Clone() - c.ctx.Fields = nil - if conn.TotalCount, err = c.Count(ctx); err != nil { - return nil, err - } - conn.PageInfo.HasNextPage = first != nil && conn.TotalCount > 0 - conn.PageInfo.HasPreviousPage = last != nil && conn.TotalCount > 0 + needTotalCount := hasCollectedField(ctx, totalCountField) + needPageInfo := hasCollectedField(ctx, pageInfoField) + hasPagination := after != nil || first != nil || before != nil || last != nil + if (needTotalCount && hasPagination) || (ignoredEdges && (needTotalCount || needPageInfo)) { + c := tm.Clone() + c.ctx.Fields = nil + if conn.TotalCount, err = c.Count(ctx); err != nil { + return nil, err } + conn.PageInfo.HasNextPage = first != nil && conn.TotalCount > 0 + conn.PageInfo.HasPreviousPage = last != nil && conn.TotalCount > 0 } if ignoredEdges || (first != nil && *first == 0) || (last != nil && *last == 0) { return conn, nil @@ -6199,17 +6199,17 @@ func (tr *TestResultQuery) Paginate( } conn := &TestResultConnection{Edges: []*TestResultEdge{}} ignoredEdges := !hasCollectedField(ctx, edgesField) - if hasCollectedField(ctx, totalCountField) || hasCollectedField(ctx, pageInfoField) { - hasPagination := after != nil || first != nil || before != nil || last != nil - if hasPagination || ignoredEdges { - c := tr.Clone() - c.ctx.Fields = nil - if conn.TotalCount, err = c.Count(ctx); err != nil { - return nil, err - } - conn.PageInfo.HasNextPage = first != nil && conn.TotalCount > 0 - conn.PageInfo.HasPreviousPage = last != nil && conn.TotalCount > 0 + needTotalCount := hasCollectedField(ctx, totalCountField) + needPageInfo := hasCollectedField(ctx, pageInfoField) + hasPagination := after != nil || first != nil || before != nil || last != nil + if (needTotalCount && hasPagination) || (ignoredEdges && (needTotalCount || needPageInfo)) { + c := tr.Clone() + c.ctx.Fields = nil + if conn.TotalCount, err = c.Count(ctx); err != nil { + return nil, err } + conn.PageInfo.HasNextPage = first != nil && conn.TotalCount > 0 + conn.PageInfo.HasPreviousPage = last != nil && conn.TotalCount > 0 } if ignoredEdges || (first != nil && *first == 0) || (last != nil && *last == 0) { return conn, nil @@ -6448,17 +6448,17 @@ func (ts *TestSummaryQuery) Paginate( } conn := &TestSummaryConnection{Edges: []*TestSummaryEdge{}} ignoredEdges := !hasCollectedField(ctx, edgesField) - if hasCollectedField(ctx, totalCountField) || hasCollectedField(ctx, pageInfoField) { - hasPagination := after != nil || first != nil || before != nil || last != nil - if hasPagination || ignoredEdges { - c := ts.Clone() - c.ctx.Fields = nil - if conn.TotalCount, err = c.Count(ctx); err != nil { - return nil, err - } - conn.PageInfo.HasNextPage = first != nil && conn.TotalCount > 0 - conn.PageInfo.HasPreviousPage = last != nil && conn.TotalCount > 0 + needTotalCount := hasCollectedField(ctx, totalCountField) + needPageInfo := hasCollectedField(ctx, pageInfoField) + hasPagination := after != nil || first != nil || before != nil || last != nil + if (needTotalCount && hasPagination) || (ignoredEdges && (needTotalCount || needPageInfo)) { + c := ts.Clone() + c.ctx.Fields = nil + if conn.TotalCount, err = c.Count(ctx); err != nil { + return nil, err } + conn.PageInfo.HasNextPage = first != nil && conn.TotalCount > 0 + conn.PageInfo.HasPreviousPage = last != nil && conn.TotalCount > 0 } if ignoredEdges || (first != nil && *first == 0) || (last != nil && *last == 0) { return conn, nil @@ -6762,17 +6762,17 @@ func (tm *TimingMetricsQuery) Paginate( } conn := &TimingMetricsConnection{Edges: []*TimingMetricsEdge{}} ignoredEdges := !hasCollectedField(ctx, edgesField) - if hasCollectedField(ctx, totalCountField) || hasCollectedField(ctx, pageInfoField) { - hasPagination := after != nil || first != nil || before != nil || last != nil - if hasPagination || ignoredEdges { - c := tm.Clone() - c.ctx.Fields = nil - if conn.TotalCount, err = c.Count(ctx); err != nil { - return nil, err - } - conn.PageInfo.HasNextPage = first != nil && conn.TotalCount > 0 - conn.PageInfo.HasPreviousPage = last != nil && conn.TotalCount > 0 + needTotalCount := hasCollectedField(ctx, totalCountField) + needPageInfo := hasCollectedField(ctx, pageInfoField) + hasPagination := after != nil || first != nil || before != nil || last != nil + if (needTotalCount && hasPagination) || (ignoredEdges && (needTotalCount || needPageInfo)) { + c := tm.Clone() + c.ctx.Fields = nil + if conn.TotalCount, err = c.Count(ctx); err != nil { + return nil, err } + conn.PageInfo.HasNextPage = first != nil && conn.TotalCount > 0 + conn.PageInfo.HasPreviousPage = last != nil && conn.TotalCount > 0 } if ignoredEdges || (first != nil && *first == 0) || (last != nil && *last == 0) { return conn, nil diff --git a/ent/template/pagination.tmpl b/ent/template/pagination.tmpl new file mode 100644 index 00000000..435170fa --- /dev/null +++ b/ent/template/pagination.tmpl @@ -0,0 +1,77 @@ +{{- /* + +This template overrides the normal entgql pagination method to avoid +unnessecary counts. + +Sometimes when calling paginate, ent needs to do a `SELECT COUNT("id")` on all +rows in a table, but sometimes it does it even when it is not needed. When the +request contains a `pageInfoField` and a `edgesField`, while not containing a +`totalCountField`, we don't have to count all rows, since we can populate the +`pageInfoField` from the returned data. This should be fixed when +https://github.com/ent/contrib/pull/625 is merged. + +This template patches the paginate method with new logic for determining when +to count all rows. This template is based on the original template in +`entgo.io/contrib v0.6.0`. When updating the version of `entgo.io/contrib`, +this template will probably need to be updated. + +*/ -}} +{{ define "gql_pagination/helper/paginate" }} + {{- $node := $.Scope.Node }} + {{- $r := $.Scope.Query }} + {{- $names := nodePaginationNames $node }} + {{- $name := $names.Node }} + {{- $order := $names.Order }} + {{- $edge := $names.Edge }} + {{- $conn := $names.Connection }} + {{- $newPager := print "new" $name "Pager" -}} + + if err := validateFirstLast(first, last); err != nil { + return nil, err + } + pager, err := {{ $newPager }}(opts, last != nil) + if err != nil { + return nil, err + } + if {{ $r }}, err = pager.applyFilter({{ $r }}); err != nil { + return nil, err + } + {{- /* Ensure the "edges" field is marshaled as "[]" in case it is empty. */}} + conn := &{{ $conn }}{Edges: []*{{ $edge }}{}} + ignoredEdges := !hasCollectedField(ctx, edgesField) + needTotalCount := hasCollectedField(ctx, totalCountField) + needPageInfo := hasCollectedField(ctx, pageInfoField) + hasPagination := after != nil || first != nil || before != nil || last != nil + if (needTotalCount && hasPagination) || (ignoredEdges && (needTotalCount || needPageInfo)) { + c := {{ $r }}.Clone() + {{- /* Clear the selection fields before counting to avoid generating invalid queries. */}} + c.ctx.Fields = nil + if conn.TotalCount, err = c.Count(ctx); err != nil { + return nil, err + } + conn.PageInfo.HasNextPage = first != nil && conn.TotalCount > 0 + conn.PageInfo.HasPreviousPage = last != nil && conn.TotalCount > 0 + } + if ignoredEdges || (first != nil && *first == 0) || (last != nil && *last == 0) { + return conn, nil + } + if {{ $r }}, err = pager.applyCursors({{ $r }}, after, before); err != nil { + return nil, err + } + limit := paginateLimit(first, last) + if limit != 0 { + {{ $r }}.Limit(limit) + } + if field := collectedField(ctx, edgesField, nodeField); field != nil { + if err := {{ $r }}.collectField(ctx, limit == 1, graphql.GetOperationContext(ctx), *field, []string{edgesField, nodeField}); err != nil { + return nil, err + } + } + {{ $r }} = pager.applyOrder({{ $r }}) + nodes, err := {{ $r }}.All(ctx) + if err != nil { + return nil, err + } + conn.build(nodes, pager, after, first, before, last) + return conn, nil +{{ end }} diff --git a/go.mod b/go.mod index a81c6008..03528b9e 100644 --- a/go.mod +++ b/go.mod @@ -15,6 +15,8 @@ replace ( ) require ( + // NOTE: When updating entgo.io/ent and entgo.io/contrib, check if any of + // the templates in `./ent/template` needs to be updated. entgo.io/contrib v0.6.0 entgo.io/ent v0.14.4 github.com/99designs/gqlgen v0.17.76