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
This commit is contained in:
@@ -87,6 +87,7 @@ export async function buildSearchParam({
|
||||
const sanitizedQueryValue = sanitizeQueryValue({
|
||||
field,
|
||||
hasCustomID,
|
||||
locale,
|
||||
operator,
|
||||
path,
|
||||
payload,
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
@@ -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'],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -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<T extends boolean = true> {
|
||||
*/
|
||||
export interface PolymorphicRelationshipsSelect<T extends boolean = true> {
|
||||
polymorphic?: T;
|
||||
polymorphicLocalized?: T;
|
||||
polymorphicMany?: T;
|
||||
polymorphicManyLocalized?: T;
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user