Skip to content

Commit c819083

Browse files
rjgtavRicardo Tavares
andauthored
perf(graphql): select only the requested columns (#13711)
Optimized database queries generated by graphQL by only selecting the fields that have been requested. This is particularly useful for collections with fields of type "join" and "relationship" Co-authored-by: Ricardo Tavares <rtavares@cloudflare.com>
1 parent 394000d commit c819083

File tree

13 files changed

+275
-112
lines changed

13 files changed

+275
-112
lines changed

packages/drizzle/src/find/traverseFields.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@ export const traverseFields = ({
292292
) {
293293
blockSelect = {}
294294
blockSelectMode = 'include'
295-
} else if (selectMode === 'include' && blocksSelect[block.slug] === true) {
295+
} else if (selectMode === 'include' && Boolean(blocksSelect[block.slug])) {
296296
blockSelect = true
297297
}
298298
}
@@ -789,7 +789,7 @@ export const traverseFields = ({
789789
if (select || selectAllOnCurrentLevel) {
790790
if (
791791
selectAllOnCurrentLevel ||
792-
(selectMode === 'include' && select[field.name] === true) ||
792+
(selectMode === 'include' && Boolean(select[field.name])) ||
793793
(selectMode === 'exclude' && typeof select[field.name] === 'undefined')
794794
) {
795795
shouldSelect = true
@@ -853,7 +853,7 @@ export const traverseFields = ({
853853

854854
if (
855855
selectAllOnCurrentLevel ||
856-
(selectMode === 'include' && select[field.name] === true) ||
856+
(selectMode === 'include' && Boolean(select[field.name])) ||
857857
(selectMode === 'exclude' && typeof select[field.name] === 'undefined')
858858
) {
859859
const fieldPath = `${path}${field.name}`
Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1-
import type { Collection, PaginatedDocs, PayloadRequest, Where } from 'payload'
1+
import type { GraphQLResolveInfo } from 'graphql'
2+
import type { Collection, PaginatedDocs, Where } from 'payload'
23

34
import { findOperation, isolateObjectProperty } from 'payload'
45

56
import type { Context } from '../types.js'
67

8+
import { buildSelectForCollectionMany } from '../../utilities/select.js'
9+
710
export type Resolver = (
811
_: unknown,
912
args: {
@@ -14,28 +17,24 @@ export type Resolver = (
1417
locale?: string
1518
page?: number
1619
pagination?: boolean
20+
select?: boolean
1721
sort?: string
1822
trash?: boolean
1923
where?: Where
2024
},
21-
context: {
22-
req: PayloadRequest
23-
},
25+
context: Context,
26+
info: GraphQLResolveInfo,
2427
// eslint-disable-next-line @typescript-eslint/no-explicit-any
2528
) => Promise<PaginatedDocs<any>>
2629

2730
export function findResolver(collection: Collection): Resolver {
28-
return async function resolver(_, args, context: Context) {
29-
let { req } = context
30-
const locale = req.locale
31-
const fallbackLocale = req.fallbackLocale
31+
return async function resolver(_, args, context, info) {
32+
const req = context.req = isolateObjectProperty(context.req, ['locale', 'fallbackLocale', 'transactionID'])
33+
const select = context.select = args.select ? buildSelectForCollectionMany(info) : undefined
3234

33-
req = isolateObjectProperty(req, ['locale', 'fallbackLocale', 'transactionID'])
34-
req.locale = args.locale || locale
35-
req.fallbackLocale = args.fallbackLocale || fallbackLocale
36-
if (!req.query) {
37-
req.query = {}
38-
}
35+
req.locale = args.locale || req.locale
36+
req.fallbackLocale = args.fallbackLocale || req.fallbackLocale
37+
req.query = req.query || {}
3938

4039
const draft: boolean =
4140
(args.draft ?? req.query?.draft === 'false')
@@ -47,8 +46,6 @@ export function findResolver(collection: Collection): Resolver {
4746
req.query.draft = String(draft)
4847
}
4948

50-
context.req = req
51-
5249
const options = {
5350
collection,
5451
depth: 0,
@@ -57,12 +54,13 @@ export function findResolver(collection: Collection): Resolver {
5754
page: args.page,
5855
pagination: args.pagination,
5956
req,
57+
select,
6058
sort: args.sort,
6159
trash: args.trash,
6260
where: args.where,
6361
}
6462

65-
const results = await findOperation(options)
66-
return results
63+
const result = await findOperation(options)
64+
return result
6765
}
6866
}
Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,36 @@
1-
import type { Collection, CollectionSlug, DataFromCollectionSlug, PayloadRequest } from 'payload'
1+
import type { GraphQLResolveInfo } from 'graphql'
2+
import type { Collection, CollectionSlug, DataFromCollectionSlug } from 'payload'
23

34
import { findByIDOperation, isolateObjectProperty } from 'payload'
45

56
import type { Context } from '../types.js'
67

8+
import { buildSelectForCollection } from '../../utilities/select.js'
9+
710
export type Resolver<TData> = (
811
_: unknown,
912
args: {
1013
draft: boolean
1114
fallbackLocale?: string
1215
id: string
1316
locale?: string
17+
select?: boolean
1418
trash?: boolean
1519
},
16-
context: {
17-
req: PayloadRequest
18-
},
20+
context: Context,
21+
info: GraphQLResolveInfo,
1922
) => Promise<TData>
2023

2124
export function findByIDResolver<TSlug extends CollectionSlug>(
2225
collection: Collection,
2326
): Resolver<DataFromCollectionSlug<TSlug>> {
24-
return async function resolver(_, args, context: Context) {
25-
let { req } = context
26-
const locale = req.locale
27-
const fallbackLocale = req.fallbackLocale
28-
req = isolateObjectProperty(req, 'locale')
29-
req = isolateObjectProperty(req, 'fallbackLocale')
30-
req.locale = args.locale || locale
31-
req.fallbackLocale = args.fallbackLocale || fallbackLocale
32-
if (!req.query) {
33-
req.query = {}
34-
}
27+
return async function resolver(_, args, context, info) {
28+
const req = context.req = isolateObjectProperty(context.req, ['locale', 'fallbackLocale', 'transactionID'])
29+
const select = context.select = args.select ? buildSelectForCollection(info) : undefined
30+
31+
req.locale = args.locale || req.locale
32+
req.fallbackLocale = args.fallbackLocale || req.fallbackLocale
33+
req.query = req.query || {}
3534

3635
const draft: boolean =
3736
(args.draft ?? req.query?.draft === 'false')
@@ -43,19 +42,17 @@ export function findByIDResolver<TSlug extends CollectionSlug>(
4342
req.query.draft = String(draft)
4443
}
4544

46-
context.req = req
47-
4845
const options = {
4946
id: args.id,
5047
collection,
5148
depth: 0,
5249
draft: args.draft,
53-
req: isolateObjectProperty(req, 'transactionID'),
50+
req,
51+
select,
5452
trash: args.trash,
5553
}
5654

5755
const result = await findByIDOperation(options)
58-
5956
return result
6057
}
6158
}
Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,44 @@
1-
import type { Collection, PayloadRequest, TypeWithID, TypeWithVersion } from 'payload'
1+
import type { GraphQLResolveInfo } from 'graphql'
2+
import type { Collection, TypeWithID, TypeWithVersion } from 'payload'
23

34
import { findVersionByIDOperation, isolateObjectProperty } from 'payload'
45

56
import type { Context } from '../types.js'
67

8+
import { buildSelectForCollection } from '../../utilities/select.js'
9+
710
export type Resolver<T extends TypeWithID = any> = (
811
_: unknown,
912
args: {
1013
fallbackLocale?: string
1114
id: number | string
1215
locale?: string
16+
select?: boolean
1317
trash?: boolean
1418
},
15-
context: {
16-
req: PayloadRequest
17-
},
19+
context: Context,
20+
info: GraphQLResolveInfo,
1821
) => Promise<TypeWithVersion<T>>
1922

2023
export function findVersionByIDResolver(collection: Collection): Resolver {
21-
return async function resolver(_, args, context: Context) {
22-
let { req } = context
23-
const locale = req.locale
24-
const fallbackLocale = req.fallbackLocale
25-
req = isolateObjectProperty(req, 'locale')
26-
req = isolateObjectProperty(req, 'fallbackLocale')
27-
req.locale = args.locale || locale
28-
req.fallbackLocale = args.fallbackLocale || fallbackLocale
24+
return async function resolver(_, args, context, info) {
25+
const req = context.req = isolateObjectProperty(context.req, ['locale', 'fallbackLocale', 'transactionID'])
26+
const select = context.select = args.select ? buildSelectForCollection(info) : undefined
2927

30-
context.req = req
28+
req.locale = args.locale || req.locale
29+
req.fallbackLocale = args.fallbackLocale || req.fallbackLocale
30+
req.query = req.query || {}
3131

3232
const options = {
3333
id: args.id,
3434
collection,
3535
depth: 0,
36-
req: isolateObjectProperty(req, 'transactionID'),
36+
req,
37+
select,
3738
trash: args.trash,
3839
}
3940

4041
const result = await findVersionByIDOperation(options)
41-
4242
return result
4343
}
4444
}
Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1-
import type { Collection, PaginatedDocs, PayloadRequest, Where } from 'payload'
1+
import type { GraphQLResolveInfo } from 'graphql'
2+
import type { Collection, PaginatedDocs, Where } from 'payload'
23

34
import { findVersionsOperation, isolateObjectProperty } from 'payload'
45

56
import type { Context } from '../types.js'
67

8+
import { buildSelectForCollectionMany } from '../../utilities/select.js'
9+
710
export type Resolver = (
811
_: unknown,
912
args: {
@@ -13,27 +16,23 @@ export type Resolver = (
1316
locale?: string
1417
page?: number
1518
pagination?: boolean
19+
select?: boolean
1620
sort?: string
1721
trash?: boolean
1822
where: Where
1923
},
20-
context: {
21-
req: PayloadRequest
22-
},
24+
context: Context,
25+
info: GraphQLResolveInfo,
2326
) => Promise<PaginatedDocs<any>>
2427

2528
export function findVersionsResolver(collection: Collection): Resolver {
26-
return async function resolver(_, args, context: Context) {
27-
let { req } = context
28-
const locale = req.locale
29-
const fallbackLocale = req.fallbackLocale
30-
req = isolateObjectProperty(req, 'locale')
31-
req = isolateObjectProperty(req, 'fallbackLocale')
32-
req.locale = args.locale || locale
33-
req.fallbackLocale = args.fallbackLocale || fallbackLocale
34-
if (!req.query) {
35-
req.query = {}
36-
}
29+
return async function resolver(_, args, context, info) {
30+
const req = context.req = isolateObjectProperty(context.req, ['locale', 'fallbackLocale', 'transactionID'])
31+
const select = context.select = args.select ? buildSelectForCollectionMany(info) : undefined
32+
33+
req.locale = args.locale || req.locale
34+
req.fallbackLocale = args.fallbackLocale || req.fallbackLocale
35+
req.query = req.query || {}
3736

3837
const draft: boolean =
3938
(args.draft ?? req.query?.draft === 'false')
@@ -45,22 +44,20 @@ export function findVersionsResolver(collection: Collection): Resolver {
4544
req.query.draft = String(draft)
4645
}
4746

48-
context.req = req
49-
5047
const options = {
5148
collection,
5249
depth: 0,
5350
limit: args.limit,
5451
page: args.page,
5552
pagination: args.pagination,
56-
req: isolateObjectProperty(req, 'transactionID'),
53+
req,
54+
select,
5755
sort: args.sort,
5856
trash: args.trash,
5957
where: args.where,
6058
}
6159

6260
const result = await findVersionsOperation(options)
63-
6461
return result
6562
}
6663
}

packages/graphql/src/resolvers/globals/findOne.ts

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,42 @@
1+
import type { GraphQLResolveInfo } from 'graphql'
12
import type { Document, SanitizedGlobalConfig } from 'payload'
23

34
import { findOneOperation, isolateObjectProperty } from 'payload'
45

56
import type { Context } from '../types.js'
67

7-
export function findOne(globalConfig: SanitizedGlobalConfig): Document {
8-
return async function resolver(_, args, context: Context) {
9-
if (args.locale) {
10-
context.req.locale = args.locale
11-
}
12-
if (args.fallbackLocale) {
13-
context.req.fallbackLocale = args.fallbackLocale
14-
}
8+
import { buildSelectForCollection } from '../../utilities/select.js'
159

10+
export type Resolver = (
11+
_: unknown,
12+
args: {
13+
draft?: boolean
14+
fallbackLocale?: string
15+
id: number | string
16+
locale?: string
17+
select?: boolean
18+
},
19+
context: Context,
20+
info: GraphQLResolveInfo
21+
) => Promise<Document>
22+
23+
export function findOne(globalConfig: SanitizedGlobalConfig): Resolver {
24+
return async function resolver(_, args, context, info) {
25+
const req = context.req = isolateObjectProperty(context.req, ['locale', 'fallbackLocale', 'transactionID'])
26+
const select = context.select = args.select ? buildSelectForCollection(info) : undefined
1627
const { slug } = globalConfig
1728

29+
req.locale = args.locale || req.locale
30+
req.fallbackLocale = args.fallbackLocale || req.fallbackLocale
31+
req.query = req.query || {}
32+
1833
const options = {
1934
slug,
2035
depth: 0,
2136
draft: args.draft,
2237
globalConfig,
23-
req: isolateObjectProperty(context.req, 'transactionID'),
38+
req,
39+
select,
2440
}
2541

2642
const result = await findOneOperation(options)

0 commit comments

Comments
 (0)