From 5753efb0a40d8ede47689a53f22f51c1215bd2e9 Mon Sep 17 00:00:00 2001 From: Sasha <64744993+r1tsuu@users.noreply.github.com> Date: Thu, 19 Dec 2024 17:42:15 +0200 Subject: [PATCH] fix(db-mongodb): querying by localized polymorphic relationships using objects (#10037) Previously, queries like this didn't work: ```ts const res = await payload.find({ collection: 'polymorphic-relationships', where: { polymorphicLocalized: { equals: { relationTo: 'movies', value: movie.id, }, }, }, }) ``` This was due to the incorrectly passed path to MongoDB without `.{locale}` suffix. Additionally, to MongoDB now we send: ``` { $or: [ { polymorphic: { $eq: { relationTo: formattedValue.relationTo, value: formattedValue.value, }, }, }, { polymorphic: { $eq: { relationTo: 'movies', value: 'some-id', }, }, }, ], }, ``` Instead of: ``` { $and: [ { 'polymorphic.relationTo': { $eq: 'movies ', }, }, { 'polymorphic.value': { $eq: 'some-id ', }, }, ], } ``` To match the _exact_ value. This is essential when we do querying by relationships with `hasMany: true` and custom IDs that can be repeated. `$or` is needed if for some reason keys are stored in the DB in a different order --- .../src/queries/buildSearchParams.ts | 1 + .../src/queries/sanitizeQueryValue.ts | 31 ++++- test/relationships/config.ts | 19 ++++ test/relationships/int.spec.ts | 106 ++++++++++++++++++ test/relationships/payload-types.ts | 19 ++++ 5 files changed, 173 insertions(+), 3 deletions(-) diff --git a/packages/db-mongodb/src/queries/buildSearchParams.ts b/packages/db-mongodb/src/queries/buildSearchParams.ts index c914b6d7d6..3f0f97fd67 100644 --- a/packages/db-mongodb/src/queries/buildSearchParams.ts +++ b/packages/db-mongodb/src/queries/buildSearchParams.ts @@ -87,6 +87,7 @@ export async function buildSearchParam({ const sanitizedQueryValue = sanitizeQueryValue({ field, hasCustomID, + locale, operator, path, payload, diff --git a/packages/db-mongodb/src/queries/sanitizeQueryValue.ts b/packages/db-mongodb/src/queries/sanitizeQueryValue.ts index 3459212325..ce8df39990 100644 --- a/packages/db-mongodb/src/queries/sanitizeQueryValue.ts +++ b/packages/db-mongodb/src/queries/sanitizeQueryValue.ts @@ -6,6 +6,7 @@ import { createArrayFromCommaDelineated } from 'payload' type SanitizeQueryValueArgs = { field: FlattenedField hasCustomID: boolean + locale?: string operator: string path: string payload: Payload @@ -74,6 +75,7 @@ const getFieldFromSegments = ({ export const sanitizeQueryValue = ({ field, hasCustomID, + locale, operator, path, payload, @@ -205,11 +207,34 @@ export const sanitizeQueryValue = ({ formattedValue.value = new Types.ObjectId(value) } + let localizedPath = path + + if (field.localized && payload.config.localization && locale) { + localizedPath = `${path}.${locale}` + } + return { rawQuery: { - $and: [ - { [`${path}.value`]: { $eq: formattedValue.value } }, - { [`${path}.relationTo`]: { $eq: formattedValue.relationTo } }, + $or: [ + { + [localizedPath]: { + $eq: { + // disable auto sort + /* eslint-disable */ + value: formattedValue.value, + relationTo: formattedValue.relationTo, + /* eslint-enable */ + }, + }, + }, + { + [localizedPath]: { + $eq: { + relationTo: formattedValue.relationTo, + value: formattedValue.value, + }, + }, + }, ], }, } diff --git a/test/relationships/config.ts b/test/relationships/config.ts index 7431af8055..aad779663b 100644 --- a/test/relationships/config.ts +++ b/test/relationships/config.ts @@ -290,6 +290,25 @@ export default buildConfigWithDefaults({ name: 'polymorphic', relationTo: ['movies'], }, + { + type: 'relationship', + name: 'polymorphicLocalized', + relationTo: ['movies'], + localized: true, + }, + { + type: 'relationship', + name: 'polymorphicMany', + hasMany: true, + relationTo: ['movies'], + }, + { + type: 'relationship', + hasMany: true, + name: 'polymorphicManyLocalized', + localized: true, + relationTo: ['movies'], + }, ], }, { diff --git a/test/relationships/int.spec.ts b/test/relationships/int.spec.ts index 33a1f29de8..43b9ec429d 100644 --- a/test/relationships/int.spec.ts +++ b/test/relationships/int.spec.ts @@ -1364,6 +1364,112 @@ describe('Relationships', () => { }) expect(res_2.docs).toHaveLength(0) }) + + it('should allow querying on hasMany polymorphic relationships with an object syntax', async () => { + const movie = await payload.create({ + collection: 'movies', + data: { + name: 'Pulp Fiction 2', + }, + }) + + const { id } = await payload.create({ + collection: polymorphicRelationshipsSlug, + data: { + polymorphicMany: [ + { + relationTo: 'movies', + value: movie.id, + }, + ], + }, + }) + + const res = await payload.find({ + collection: 'polymorphic-relationships', + where: { + polymorphicMany: { + equals: { + relationTo: 'movies', + value: movie.id, + }, + }, + }, + }) + + expect(res.docs).toHaveLength(1) + expect(res.docs[0].id).toBe(id) + }) + + it('should allow querying on localized polymorphic relationships with an object syntax', async () => { + const movie = await payload.create({ + collection: 'movies', + data: { + name: 'Pulp Fiction 2', + }, + }) + + const { id } = await payload.create({ + collection: polymorphicRelationshipsSlug, + data: { + polymorphicLocalized: { + relationTo: 'movies', + value: movie.id, + }, + }, + }) + + const res = await payload.find({ + collection: 'polymorphic-relationships', + where: { + polymorphicLocalized: { + equals: { + relationTo: 'movies', + value: movie.id, + }, + }, + }, + }) + + expect(res.docs).toHaveLength(1) + expect(res.docs[0].id).toBe(id) + }) + + it('should allow querying on hasMany localized polymorphic relationships with an object syntax', async () => { + const movie = await payload.create({ + collection: 'movies', + data: { + name: 'Pulp Fiction 2', + }, + }) + + const { id } = await payload.create({ + collection: polymorphicRelationshipsSlug, + data: { + polymorphicManyLocalized: [ + { + relationTo: 'movies', + value: movie.id, + }, + ], + }, + }) + + const res = await payload.find({ + collection: 'polymorphic-relationships', + where: { + polymorphicManyLocalized: { + equals: { + relationTo: 'movies', + value: movie.id, + }, + }, + }, + }) + + expect(res.docs).toHaveLength(1) + expect(res.docs[0].id).toBe(id) + }) }) }) diff --git a/test/relationships/payload-types.ts b/test/relationships/payload-types.ts index c0581e5cc6..6cc5ab11ef 100644 --- a/test/relationships/payload-types.ts +++ b/test/relationships/payload-types.ts @@ -252,6 +252,22 @@ export interface PolymorphicRelationship { relationTo: 'movies'; value: string | Movie; } | null; + polymorphicLocalized?: { + relationTo: 'movies'; + value: string | Movie; + } | null; + polymorphicMany?: + | { + relationTo: 'movies'; + value: string | Movie; + }[] + | null; + polymorphicManyLocalized?: + | { + relationTo: 'movies'; + value: string | Movie; + }[] + | null; updatedAt: string; createdAt: string; } @@ -591,6 +607,9 @@ export interface MovieReviewsSelect { */ export interface PolymorphicRelationshipsSelect { polymorphic?: T; + polymorphicLocalized?: T; + polymorphicMany?: T; + polymorphicManyLocalized?: T; updatedAt?: T; createdAt?: T; }