feat(db-*): support limit in db.updateMany (#11488)

This PR adds a new `limit` property to `payload.db.updateMany`. This functionality is required for [migrating our job system to use faster, direct db adapter calls](https://github.com/payloadcms/payload/pull/11489)
This commit is contained in:
Alessio Gravili
2025-03-02 22:32:57 -07:00
committed by GitHub
parent 192964417d
commit 6a3d58bb32
4 changed files with 209 additions and 7 deletions

View File

@@ -15,6 +15,7 @@ export const updateMany: UpdateMany = async function updateMany(
{
collection: collectionSlug,
data,
limit,
locale,
options: optionsArgs = {},
req,
@@ -37,7 +38,7 @@ export const updateMany: UpdateMany = async function updateMany(
session: await getSession(this, req),
}
const query = await buildQuery({
let query = await buildQuery({
adapter: this,
collectionSlug,
fields: collectionConfig.flattenedFields,
@@ -48,6 +49,19 @@ export const updateMany: UpdateMany = async function updateMany(
transform({ adapter: this, data, fields: collectionConfig.fields, operation: 'write' })
try {
if (typeof limit === 'number' && limit > 0) {
const documentsToUpdate = await Model.find(
query,
{},
{ ...options, limit, projection: { _id: 1 } },
)
if (documentsToUpdate.length === 0) {
return null
}
query = { _id: { $in: documentsToUpdate.map((doc) => doc._id) } }
}
await Model.updateMany(query, data, options)
} catch (error) {
handleError({ collection: collectionSlug, error, req })

View File

@@ -16,6 +16,7 @@ export const updateMany: UpdateMany = async function updateMany(
collection: collectionSlug,
data,
joins: joinQuery,
limit,
locale,
req,
returning,
@@ -55,7 +56,16 @@ export const updateMany: UpdateMany = async function updateMany(
const table = this.tables[tableName]
const docsToUpdate = await _db
const docsToUpdate =
typeof limit === 'number' && limit > 0
? await _db
.select({
id: table.id,
})
.from(table)
.where(where)
.limit(limit)
: await _db
.select({
id: table.id,
})

View File

@@ -517,6 +517,7 @@ export type UpdateManyArgs = {
data: Record<string, unknown>
draft?: boolean
joins?: JoinQuery
limit?: number
locale?: string
/**
* Additional database adapter specific options to pass to the query

View File

@@ -960,6 +960,183 @@ describe('database', () => {
expect(notUpdatedDocs).toHaveLength(1)
expect(notUpdatedDocs?.[0]?.title).toBe('notupdated')
})
it('ensure updateMany respects limit', async () => {
await payload.db.deleteMany({
collection: postsSlug,
where: {
id: {
exists: true,
},
},
})
// Create 11 posts
for (let i = 0; i < 11; i++) {
await payload.create({
collection: postsSlug,
data: {
title: 'not updated',
},
})
}
const result = await payload.db.updateMany({
collection: postsSlug,
data: {
title: 'updated',
},
limit: 5,
where: {
id: {
exists: true,
},
},
})
expect(result?.length).toBe(5)
expect(result?.[0]?.title).toBe('updated')
expect(result?.[4]?.title).toBe('updated')
// Ensure all posts minus the one we don't want updated are updated
const { docs } = await payload.find({
collection: postsSlug,
depth: 0,
pagination: false,
where: {
title: {
equals: 'updated',
},
},
})
expect(docs).toHaveLength(5)
expect(docs?.[0]?.title).toBe('updated')
expect(docs?.[4]?.title).toBe('updated')
const { docs: notUpdatedDocs } = await payload.find({
collection: postsSlug,
depth: 0,
pagination: false,
where: {
title: {
equals: 'not updated',
},
},
})
expect(notUpdatedDocs).toHaveLength(6)
expect(notUpdatedDocs?.[0]?.title).toBe('not updated')
expect(notUpdatedDocs?.[5]?.title).toBe('not updated')
})
it('ensure updateMany correctly handles 0 limit', async () => {
await payload.db.deleteMany({
collection: postsSlug,
where: {
id: {
exists: true,
},
},
})
// Create 5 posts
for (let i = 0; i < 5; i++) {
await payload.create({
collection: postsSlug,
data: {
title: 'not updated',
},
})
}
const result = await payload.db.updateMany({
collection: postsSlug,
data: {
title: 'updated',
},
limit: 0,
where: {
id: {
exists: true,
},
},
})
expect(result?.length).toBe(5)
expect(result?.[0]?.title).toBe('updated')
expect(result?.[4]?.title).toBe('updated')
// Ensure all posts are updated. limit: 0 should mean unlimited
const { docs } = await payload.find({
collection: postsSlug,
depth: 0,
pagination: false,
where: {
title: {
equals: 'updated',
},
},
})
expect(docs).toHaveLength(5)
expect(docs?.[0]?.title).toBe('updated')
expect(docs?.[4]?.title).toBe('updated')
})
it('ensure updateMany correctly handles -1 limit', async () => {
await payload.db.deleteMany({
collection: postsSlug,
where: {
id: {
exists: true,
},
},
})
// Create 5 posts
for (let i = 0; i < 5; i++) {
await payload.create({
collection: postsSlug,
data: {
title: 'not updated',
},
})
}
const result = await payload.db.updateMany({
collection: postsSlug,
data: {
title: 'updated',
},
limit: -1,
where: {
id: {
exists: true,
},
},
})
expect(result?.length).toBe(5)
expect(result?.[0]?.title).toBe('updated')
expect(result?.[4]?.title).toBe('updated')
// Ensure all posts are updated. limit: -1 should mean unlimited
const { docs } = await payload.find({
collection: postsSlug,
depth: 0,
pagination: false,
where: {
title: {
equals: 'updated',
},
},
})
expect(docs).toHaveLength(5)
expect(docs?.[0]?.title).toBe('updated')
expect(docs?.[4]?.title).toBe('updated')
})
})
describe('Error Handler', () => {