Skip to content

Commit b925d34

Browse files
authored
fix(graphql): sort by multiple fields (#14486)
Fixes handling of the [sort by multiple fields] feature (#8799) for GraphQL, now you can specify `query { Posts(sort: "field1, field2") ..` Closes #14450.
1 parent f67d031 commit b925d34

File tree

7 files changed

+6075
-2297
lines changed

7 files changed

+6075
-2297
lines changed

packages/graphql/src/resolvers/collections/find.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,12 @@ export type Resolver = (
2929

3030
export function findResolver(collection: Collection): Resolver {
3131
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
32+
const req = (context.req = isolateObjectProperty(context.req, [
33+
'locale',
34+
'fallbackLocale',
35+
'transactionID',
36+
]))
37+
const select = (context.select = args.select ? buildSelectForCollectionMany(info) : undefined)
3438

3539
req.locale = args.locale || req.locale
3640
req.fallbackLocale = args.fallbackLocale || req.fallbackLocale
@@ -46,6 +50,8 @@ export function findResolver(collection: Collection): Resolver {
4650
req.query.draft = String(draft)
4751
}
4852

53+
const { sort } = args
54+
4955
const options = {
5056
collection,
5157
depth: 0,
@@ -55,7 +61,7 @@ export function findResolver(collection: Collection): Resolver {
5561
pagination: args.pagination,
5662
req,
5763
select,
58-
sort: args.sort,
64+
sort: typeof sort === 'string' ? sort.split(',') : undefined,
5965
trash: args.trash,
6066
where: args.where,
6167
}

packages/graphql/src/resolvers/collections/findVersions.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,12 @@ export type Resolver = (
2727

2828
export function findVersionsResolver(collection: Collection): Resolver {
2929
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
30+
const req = (context.req = isolateObjectProperty(context.req, [
31+
'locale',
32+
'fallbackLocale',
33+
'transactionID',
34+
]))
35+
const select = (context.select = args.select ? buildSelectForCollectionMany(info) : undefined)
3236

3337
req.locale = args.locale || req.locale
3438
req.fallbackLocale = args.fallbackLocale || req.fallbackLocale
@@ -44,6 +48,8 @@ export function findVersionsResolver(collection: Collection): Resolver {
4448
req.query.draft = String(draft)
4549
}
4650

51+
const { sort } = args
52+
4753
const options = {
4854
collection,
4955
depth: 0,
@@ -52,7 +58,7 @@ export function findVersionsResolver(collection: Collection): Resolver {
5258
pagination: args.pagination,
5359
req,
5460
select,
55-
sort: args.sort,
61+
sort: typeof sort === 'string' ? sort.split(',') : undefined,
5662
trash: args.trash,
5763
where: args.where,
5864
}

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

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,24 @@ export type Resolver = (
2020
where: Where
2121
},
2222
context: Context,
23-
info: GraphQLResolveInfo
23+
info: GraphQLResolveInfo,
2424
) => Promise<Document>
2525

2626
export function findVersions(globalConfig: SanitizedGlobalConfig): Resolver {
2727
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 ? buildSelectForCollectionMany(info) : undefined
28+
const req = (context.req = isolateObjectProperty(context.req, [
29+
'locale',
30+
'fallbackLocale',
31+
'transactionID',
32+
]))
33+
const select = (context.select = args.select ? buildSelectForCollectionMany(info) : undefined)
3034

3135
req.locale = args.locale || req.locale
3236
req.fallbackLocale = args.fallbackLocale || req.fallbackLocale
3337
req.query = req.query || {}
3438

39+
const { sort } = args
40+
3541
const options = {
3642
depth: 0,
3743
globalConfig,
@@ -40,7 +46,7 @@ export function findVersions(globalConfig: SanitizedGlobalConfig): Resolver {
4046
pagination: args.pagination,
4147
req,
4248
select,
43-
sort: args.sort,
49+
sort: typeof sort === 'string' ? sort.split(',') : undefined,
4450
where: args.where,
4551
}
4652

test/collections-graphql/config.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,19 @@ export default buildConfigWithDefaults({
390390
],
391391
upload: true,
392392
},
393+
{
394+
slug: 'sort',
395+
fields: [
396+
{
397+
name: 'title',
398+
type: 'text',
399+
},
400+
{
401+
name: 'number',
402+
type: 'number',
403+
},
404+
],
405+
},
393406
],
394407
graphQL: {
395408
queries: (GraphQL) => {

test/collections-graphql/int.spec.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,30 @@ describe('collections-graphql', () => {
111111
expect(docs).toContainEqual(expect.objectContaining({ id: existingDoc.id }))
112112
})
113113

114+
it('should sort by multiple fields', async () => {
115+
const doc1 = await payload.create({ collection: 'sort', data: { title: 'a', number: 1 } })
116+
const doc2 = await payload.create({ collection: 'sort', data: { title: 'b', number: 1 } })
117+
const doc3 = await payload.create({ collection: 'sort', data: { title: 'a', number: 2 } })
118+
const doc4 = await payload.create({ collection: 'sort', data: { title: 'b', number: 3 } })
119+
120+
const query = `query {
121+
Sorts(sort: "title, number") {
122+
docs {
123+
id
124+
title
125+
number
126+
}
127+
}
128+
}`
129+
130+
const { data } = await restClient
131+
.GRAPHQL_POST({ body: JSON.stringify({ query }) })
132+
.then((res) => res.json())
133+
const { docs } = data.Sorts
134+
135+
expect(docs.map((doc) => doc.id)).toEqual([doc3.id, doc1.id, doc4.id, doc2.id])
136+
})
137+
114138
it('should count', async () => {
115139
const query = `query {
116140
countPosts {

test/collections-graphql/payload-types.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ export interface Config {
7979
'content-type': ContentType;
8080
'cyclical-relationship': CyclicalRelationship;
8181
media: Media;
82+
sort: Sort;
83+
'payload-kv': PayloadKv;
8284
'payload-locked-documents': PayloadLockedDocument;
8385
'payload-preferences': PayloadPreference;
8486
'payload-migrations': PayloadMigration;
@@ -97,6 +99,8 @@ export interface Config {
9799
'content-type': ContentTypeSelect<false> | ContentTypeSelect<true>;
98100
'cyclical-relationship': CyclicalRelationshipSelect<false> | CyclicalRelationshipSelect<true>;
99101
media: MediaSelect<false> | MediaSelect<true>;
102+
sort: SortSelect<false> | SortSelect<true>;
103+
'payload-kv': PayloadKvSelect<false> | PayloadKvSelect<true>;
100104
'payload-locked-documents': PayloadLockedDocumentsSelect<false> | PayloadLockedDocumentsSelect<true>;
101105
'payload-preferences': PayloadPreferencesSelect<false> | PayloadPreferencesSelect<true>;
102106
'payload-migrations': PayloadMigrationsSelect<false> | PayloadMigrationsSelect<true>;
@@ -333,6 +337,34 @@ export interface Media {
333337
focalX?: number | null;
334338
focalY?: number | null;
335339
}
340+
/**
341+
* This interface was referenced by `Config`'s JSON-Schema
342+
* via the `definition` "sort".
343+
*/
344+
export interface Sort {
345+
id: string;
346+
title?: string | null;
347+
number?: number | null;
348+
updatedAt: string;
349+
createdAt: string;
350+
}
351+
/**
352+
* This interface was referenced by `Config`'s JSON-Schema
353+
* via the `definition` "payload-kv".
354+
*/
355+
export interface PayloadKv {
356+
id: string;
357+
key: string;
358+
data:
359+
| {
360+
[k: string]: unknown;
361+
}
362+
| unknown[]
363+
| string
364+
| number
365+
| boolean
366+
| null;
367+
}
336368
/**
337369
* This interface was referenced by `Config`'s JSON-Schema
338370
* via the `definition` "payload-locked-documents".
@@ -387,6 +419,10 @@ export interface PayloadLockedDocument {
387419
| ({
388420
relationTo: 'media';
389421
value: string | Media;
422+
} | null)
423+
| ({
424+
relationTo: 'sort';
425+
value: string | Sort;
390426
} | null);
391427
globalSlug?: string | null;
392428
user: {
@@ -609,6 +645,24 @@ export interface MediaSelect<T extends boolean = true> {
609645
focalX?: T;
610646
focalY?: T;
611647
}
648+
/**
649+
* This interface was referenced by `Config`'s JSON-Schema
650+
* via the `definition` "sort_select".
651+
*/
652+
export interface SortSelect<T extends boolean = true> {
653+
title?: T;
654+
number?: T;
655+
updatedAt?: T;
656+
createdAt?: T;
657+
}
658+
/**
659+
* This interface was referenced by `Config`'s JSON-Schema
660+
* via the `definition` "payload-kv_select".
661+
*/
662+
export interface PayloadKvSelect<T extends boolean = true> {
663+
key?: T;
664+
data?: T;
665+
}
612666
/**
613667
* This interface was referenced by `Config`'s JSON-Schema
614668
* via the `definition` "payload-locked-documents_select".

0 commit comments

Comments
 (0)