Skip to content

Commit 8533dfb

Browse files
GermanJabloed-cscostar1tsuu
authored
fix(db-mongodb): remove duplicate IDs in nested relationship queries (#17019)
Backport of #16354 to 3.x. ## Summary When querying through a nested relationship (e.g. `where: { 'movie.name': { equals: '...' } }`), the `$in` array used to filter parent collection documents was populated with duplicate entries: each document ID was pushed twice, once as a string and once as a `Types.ObjectId`. Mongoose auto-casts 24-hex strings to ObjectId, so both resolve to the same value, doubling the size of every `$in` query (100 IDs -> 200 entries), causing larger queries, slower planning and, in extreme cases, client disconnects. The fix removes the redundant string push in `buildSearchParams.ts` and keeps only `doc._id`, which `.lean()` already returns as the correct BSON type. ## Test plan - Regression test in `test/relationships/int.spec.ts` that queries through a nested relationship with `or` conditions and asserts no duplicate documents are returned. Clean cherry-pick of the original commit, identical diff (+33/-2). --------- Co-authored-by: Eduardo Costa <ed_cscosta@hotmail.com> Co-authored-by: Sasha Rakhmatulin <sasha@ritsuko.dev> Co-authored-by: German Jablonski <GermanJablo@users.noreply.github.com>
1 parent b681448 commit 8533dfb

2 files changed

Lines changed: 33 additions & 2 deletions

File tree

packages/db-mongodb/src/queries/buildSearchParams.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -217,10 +217,10 @@ export async function buildSearchParam({
217217
}
218218
} else {
219219
const stringID = doc._id.toString()
220-
$in.push(stringID)
221-
222220
if (Types.ObjectId.isValid(stringID)) {
223221
$in.push(doc._id)
222+
} else {
223+
$in.push(stringID)
224224
}
225225
}
226226
})

test/relationships/int.spec.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,37 @@ describe('Relationships', () => {
473473
expect(res.docs[0].id).toBe(director_2.id)
474474
})
475475

476+
// MongoDB dedupes $in at execution, so the bug is only visible in the
477+
// filter Payload hands to Mongoose — not in the returned docs.
478+
mongoIt('should not duplicate IDs in $in when querying through a relationship', async () => {
479+
const movie = await payload.create({
480+
collection: 'movies',
481+
data: { name: 'dup_test_movie' },
482+
})
483+
484+
const Model = (payload.db as any).collections.directors
485+
const originalPaginate = Model.paginate.bind(Model)
486+
let capturedQuery: any
487+
Model.paginate = (query: any, ...rest: any[]) => {
488+
capturedQuery = query
489+
return originalPaginate(query, ...rest)
490+
}
491+
492+
try {
493+
await payload.find({
494+
collection: 'directors',
495+
where: { 'movie.name': { equals: 'dup_test_movie' } },
496+
})
497+
} finally {
498+
Model.paginate = originalPaginate
499+
}
500+
501+
// eslint-disable-next-line vitest/no-standalone-expect
502+
expect(capturedQuery.$and[0].movie.$in).toHaveLength(1)
503+
504+
await payload.delete({ collection: 'movies', id: movie.id })
505+
})
506+
476507
describe('hasMany relationships', () => {
477508
it('should retrieve totalDocs correctly with hasMany,', async () => {
478509
const movie1 = await payload.create({

0 commit comments

Comments
 (0)