Skip to content

Commit 20e975b

Browse files
authored
feat: sort support for payload.update operation (#11769)
Continuation of #11768. This adds support for `sort` in `payload.update`. ## Example ```ts const { docs } = await payload.update({ collection: 'posts', data: { title: 'updated', }, limit: 5, sort: '-numberField', // <= new where: { id: { exists: true, }, }, }) ```
1 parent e96d3c8 commit 20e975b

File tree

4 files changed

+163
-2
lines changed

4 files changed

+163
-2
lines changed

packages/payload/src/collections/endpoints/update.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,14 @@ import { updateOperation } from '../operations/update.js'
1414

1515
export const updateHandler: PayloadHandler = async (req) => {
1616
const collection = getRequestCollection(req)
17-
const { depth, draft, limit, overrideLock, populate, select, where } = req.query as {
17+
const { depth, draft, limit, overrideLock, populate, select, sort, where } = req.query as {
1818
depth?: string
1919
draft?: string
2020
limit?: string
2121
overrideLock?: string
2222
populate?: Record<string, unknown>
2323
select?: Record<string, unknown>
24+
sort?: string
2425
where?: Where
2526
}
2627

@@ -34,6 +35,7 @@ export const updateHandler: PayloadHandler = async (req) => {
3435
populate: sanitizePopulateParam(populate),
3536
req,
3637
select: sanitizeSelectParam(select),
38+
sort: typeof sort === 'string' ? sort.split(',') : undefined,
3739
where,
3840
})
3941

packages/payload/src/collections/operations/local/update.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import type {
77
PayloadRequest,
88
PopulateType,
99
SelectType,
10+
Sort,
1011
TransformCollectionWithSelect,
1112
Where,
1213
} from '../../../types/index.js'
@@ -130,6 +131,12 @@ export type ByIDOptions<
130131
* Limit documents to update
131132
*/
132133
limit?: never
134+
/**
135+
* Sort the documents, can be a string or an array of strings
136+
* @example '-createdAt' // Sort DESC by createdAt
137+
* @example ['group', '-createdAt'] // sort by 2 fields, ASC group and DESC createdAt
138+
*/
139+
sort?: never
133140
/**
134141
* A filter [query](https://payloadcms.com/docs/queries/overview)
135142
*/
@@ -148,6 +155,12 @@ export type ManyOptions<
148155
* Limit documents to update
149156
*/
150157
limit?: number
158+
/**
159+
* Sort the documents, can be a string or an array of strings
160+
* @example '-createdAt' // Sort DESC by createdAt
161+
* @example ['group', '-createdAt'] // sort by 2 fields, ASC group and DESC createdAt
162+
*/
163+
sort?: Sort
151164
/**
152165
* A filter [query](https://payloadcms.com/docs/queries/overview)
153166
*/
@@ -205,6 +218,7 @@ async function updateLocal<
205218
publishSpecificLocale,
206219
select,
207220
showHiddenFields,
221+
sort,
208222
where,
209223
} = options
210224

@@ -237,6 +251,7 @@ async function updateLocal<
237251
req,
238252
select,
239253
showHiddenFields,
254+
sort,
240255
where,
241256
}
242257

packages/payload/src/collections/operations/update.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import type { DeepPartial } from 'ts-essentials'
44
import { status as httpStatus } from 'http-status'
55

66
import type { AccessResult } from '../../config/types.js'
7-
import type { PayloadRequest, PopulateType, SelectType, Where } from '../../types/index.js'
7+
import type { PayloadRequest, PopulateType, SelectType, Sort, Where } from '../../types/index.js'
88
import type {
99
BulkOperationResult,
1010
Collection,
@@ -26,6 +26,7 @@ import { killTransaction } from '../../utilities/killTransaction.js'
2626
import { sanitizeSelect } from '../../utilities/sanitizeSelect.js'
2727
import { buildVersionCollectionFields } from '../../versions/buildCollectionFields.js'
2828
import { appendVersionToQueryKey } from '../../versions/drafts/appendVersionToQueryKey.js'
29+
import { getQueryDraftsSort } from '../../versions/drafts/getQueryDraftsSort.js'
2930
import { updateDocument } from './utilities/update.js'
3031
import { buildAfterOperation } from './utils.js'
3132

@@ -45,6 +46,12 @@ export type Arguments<TSlug extends CollectionSlug> = {
4546
req: PayloadRequest
4647
select?: SelectType
4748
showHiddenFields?: boolean
49+
/**
50+
* Sort the documents, can be a string or an array of strings
51+
* @example '-createdAt' // Sort DESC by createdAt
52+
* @example ['group', '-createdAt'] // sort by 2 fields, ASC group and DESC createdAt
53+
*/
54+
sort?: Sort
4855
where: Where
4956
}
5057

@@ -96,6 +103,7 @@ export const updateOperation = async <
96103
req,
97104
select: incomingSelect,
98105
showHiddenFields,
106+
sort,
99107
where,
100108
} = args
101109

@@ -147,6 +155,7 @@ export const updateOperation = async <
147155
locale,
148156
pagination: false,
149157
req,
158+
sort: getQueryDraftsSort({ collectionConfig, sort }),
150159
where: versionsWhere,
151160
})
152161

@@ -158,6 +167,7 @@ export const updateOperation = async <
158167
locale,
159168
pagination: false,
160169
req,
170+
sort,
161171
where: fullWhere,
162172
})
163173

test/database/int.spec.ts

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1235,6 +1235,73 @@ describe('database', () => {
12351235
}
12361236
})
12371237

1238+
it('ensure payload.update operation respects limit and sort', async () => {
1239+
await payload.db.deleteMany({
1240+
collection: postsSlug,
1241+
where: {
1242+
id: {
1243+
exists: true,
1244+
},
1245+
},
1246+
})
1247+
1248+
const numbers = Array.from({ length: 11 }, (_, i) => i)
1249+
1250+
// shuffle the numbers
1251+
numbers.sort(() => Math.random() - 0.5)
1252+
1253+
// create 11 documents numbered 0-10, but in random order
1254+
for (const i of numbers) {
1255+
await payload.create({
1256+
collection: postsSlug,
1257+
data: {
1258+
title: 'not updated',
1259+
number: i,
1260+
},
1261+
})
1262+
}
1263+
1264+
const result = await payload.update({
1265+
collection: postsSlug,
1266+
data: {
1267+
title: 'updated',
1268+
},
1269+
limit: 5,
1270+
sort: 'number',
1271+
where: {
1272+
id: {
1273+
exists: true,
1274+
},
1275+
},
1276+
})
1277+
1278+
expect(result?.docs.length).toBe(5)
1279+
1280+
for (let i = 0; i < 5; i++) {
1281+
expect(result?.docs?.[i]?.number).toBe(i)
1282+
expect(result?.docs?.[i]?.title).toBe('updated')
1283+
}
1284+
1285+
// Ensure all posts minus the one we don't want updated are updated
1286+
const { docs } = await payload.find({
1287+
collection: postsSlug,
1288+
depth: 0,
1289+
pagination: false,
1290+
sort: 'number',
1291+
where: {
1292+
title: {
1293+
equals: 'updated',
1294+
},
1295+
},
1296+
})
1297+
1298+
expect(docs).toHaveLength(5)
1299+
for (let i = 0; i < 5; i++) {
1300+
expect(docs?.[i]?.number).toBe(i)
1301+
expect(docs?.[i]?.title).toBe('updated')
1302+
}
1303+
})
1304+
12381305
it('ensure updateMany respects limit and negative sort', async () => {
12391306
await payload.db.deleteMany({
12401307
collection: postsSlug,
@@ -1302,6 +1369,73 @@ describe('database', () => {
13021369
}
13031370
})
13041371

1372+
it('ensure payload.update operation respects limit and negative sort', async () => {
1373+
await payload.db.deleteMany({
1374+
collection: postsSlug,
1375+
where: {
1376+
id: {
1377+
exists: true,
1378+
},
1379+
},
1380+
})
1381+
1382+
const numbers = Array.from({ length: 11 }, (_, i) => i)
1383+
1384+
// shuffle the numbers
1385+
numbers.sort(() => Math.random() - 0.5)
1386+
1387+
// create 11 documents numbered 0-10, but in random order
1388+
for (const i of numbers) {
1389+
await payload.create({
1390+
collection: postsSlug,
1391+
data: {
1392+
title: 'not updated',
1393+
number: i,
1394+
},
1395+
})
1396+
}
1397+
1398+
const result = await payload.update({
1399+
collection: postsSlug,
1400+
data: {
1401+
title: 'updated',
1402+
},
1403+
limit: 5,
1404+
sort: '-number',
1405+
where: {
1406+
id: {
1407+
exists: true,
1408+
},
1409+
},
1410+
})
1411+
1412+
expect(result?.docs?.length).toBe(5)
1413+
1414+
for (let i = 10; i > 5; i--) {
1415+
expect(result?.docs?.[-i + 10]?.number).toBe(i)
1416+
expect(result?.docs?.[-i + 10]?.title).toBe('updated')
1417+
}
1418+
1419+
// Ensure all posts minus the one we don't want updated are updated
1420+
const { docs } = await payload.find({
1421+
collection: postsSlug,
1422+
depth: 0,
1423+
pagination: false,
1424+
sort: '-number',
1425+
where: {
1426+
title: {
1427+
equals: 'updated',
1428+
},
1429+
},
1430+
})
1431+
1432+
expect(docs).toHaveLength(5)
1433+
for (let i = 10; i > 5; i--) {
1434+
expect(docs?.[-i + 10]?.number).toBe(i)
1435+
expect(docs?.[-i + 10]?.title).toBe('updated')
1436+
}
1437+
})
1438+
13051439
it('ensure updateMany correctly handles 0 limit', async () => {
13061440
await payload.db.deleteMany({
13071441
collection: postsSlug,

0 commit comments

Comments
 (0)