fix(db-mongodb): querying polymorphic relationships with the all operator (#10704)
Fixes https://github.com/payloadcms/payload/issues/10678
This commit is contained in:
@@ -40,7 +40,7 @@ _The exact query syntax will depend on the API you are using, but the concepts a
|
|||||||
The following operators are available for use in queries:
|
The following operators are available for use in queries:
|
||||||
|
|
||||||
| Operator | Description |
|
| Operator | Description |
|
||||||
| -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
| -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| `equals` | The value must be exactly equal. |
|
| `equals` | The value must be exactly equal. |
|
||||||
| `not_equals` | The query will return all documents where the value is not equal. |
|
| `not_equals` | The query will return all documents where the value is not equal. |
|
||||||
| `greater_than` | For numeric or date-based fields. |
|
| `greater_than` | For numeric or date-based fields. |
|
||||||
@@ -51,7 +51,7 @@ The following operators are available for use in queries:
|
|||||||
| `contains` | Must contain the value entered, case-insensitive. |
|
| `contains` | Must contain the value entered, case-insensitive. |
|
||||||
| `in` | The value must be found within the provided comma-delimited list of values. |
|
| `in` | The value must be found within the provided comma-delimited list of values. |
|
||||||
| `not_in` | The value must NOT be within the provided comma-delimited list of values. |
|
| `not_in` | The value must NOT be within the provided comma-delimited list of values. |
|
||||||
| `all` | The value must contain all values provided in the comma-delimited list. |
|
| `all` | The value must contain all values provided in the comma-delimited list. Note: currently this operator is supported only with the MongoDB adapter. |
|
||||||
| `exists` | Only return documents where the value either exists (`true`) or does not exist (`false`). |
|
| `exists` | Only return documents where the value either exists (`true`) or does not exist (`false`). |
|
||||||
| `near` | For distance related to a [Point Field](../fields/point) comma separated as `<longitude>, <latitude>, <maxDistance in meters (nullable)>, <minDistance in meters (nullable)>`. |
|
| `near` | For distance related to a [Point Field](../fields/point) comma separated as `<longitude>, <latitude>, <maxDistance in meters (nullable)>, <minDistance in meters (nullable)>`. |
|
||||||
| `within` | For [Point Fields](../fields/point) to filter documents based on whether points are inside of the given area defined in GeoJSON. [Example](../fields/point#querying-within) |
|
| `within` | For [Point Fields](../fields/point) to filter documents based on whether points are inside of the given area defined in GeoJSON. [Example](../fields/point#querying-within) |
|
||||||
|
|||||||
@@ -324,6 +324,19 @@ export const sanitizeQueryValue = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
operator === 'all' &&
|
||||||
|
Array.isArray(relationTo) &&
|
||||||
|
path.endsWith('.value') &&
|
||||||
|
Array.isArray(formattedValue)
|
||||||
|
) {
|
||||||
|
formattedValue.forEach((v, i) => {
|
||||||
|
if (Types.ObjectId.isValid(v)) {
|
||||||
|
formattedValue[i] = new Types.ObjectId(v)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up specific formatting necessary by operators
|
// Set up specific formatting necessary by operators
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ export const operatorMap: Operators = {
|
|||||||
less_than_equal: lte,
|
less_than_equal: lte,
|
||||||
like: ilike,
|
like: ilike,
|
||||||
not_equals: ne,
|
not_equals: ne,
|
||||||
|
// TODO: support this
|
||||||
// all: all,
|
// all: all,
|
||||||
not_in: notInArray,
|
not_in: notInArray,
|
||||||
or,
|
or,
|
||||||
|
|||||||
@@ -39,6 +39,8 @@ const dirname = path.dirname(filename)
|
|||||||
|
|
||||||
type EasierChained = { id: string; relation: EasierChained }
|
type EasierChained = { id: string; relation: EasierChained }
|
||||||
|
|
||||||
|
const mongoIt = process.env.PAYLOAD_DATABASE === 'mongodb' ? it : it.skip
|
||||||
|
|
||||||
describe('Relationships', () => {
|
describe('Relationships', () => {
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
;({ payload, restClient } = await initPayloadInt(dirname))
|
;({ payload, restClient } = await initPayloadInt(dirname))
|
||||||
@@ -459,6 +461,46 @@ describe('Relationships', () => {
|
|||||||
expect(query2.totalDocs).toStrictEqual(2)
|
expect(query2.totalDocs).toStrictEqual(2)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// all operator is not supported in Postgres yet for any fields
|
||||||
|
mongoIt('should query using "all" by hasMany relationship field', async () => {
|
||||||
|
const movie1 = await payload.create({
|
||||||
|
collection: 'movies',
|
||||||
|
data: {},
|
||||||
|
})
|
||||||
|
const movie2 = await payload.create({
|
||||||
|
collection: 'movies',
|
||||||
|
data: {},
|
||||||
|
})
|
||||||
|
|
||||||
|
await payload.create({
|
||||||
|
collection: 'directors',
|
||||||
|
data: {
|
||||||
|
name: 'Quentin Tarantino',
|
||||||
|
movies: [movie2.id, movie1.id],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
await payload.create({
|
||||||
|
collection: 'directors',
|
||||||
|
data: {
|
||||||
|
name: 'Quentin Tarantino',
|
||||||
|
movies: [movie2.id],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const query1 = await payload.find({
|
||||||
|
collection: 'directors',
|
||||||
|
depth: 0,
|
||||||
|
where: {
|
||||||
|
movies: {
|
||||||
|
all: [movie1.id],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(query1.totalDocs).toStrictEqual(1)
|
||||||
|
})
|
||||||
|
|
||||||
it('should sort by a property of a hasMany relationship', async () => {
|
it('should sort by a property of a hasMany relationship', async () => {
|
||||||
// no support for sort by relation in mongodb
|
// no support for sort by relation in mongodb
|
||||||
if (isMongoose(payload)) {
|
if (isMongoose(payload)) {
|
||||||
@@ -1352,6 +1394,39 @@ describe('Relationships', () => {
|
|||||||
expect(queryTwo.docs).toHaveLength(1)
|
expect(queryTwo.docs).toHaveLength(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// all operator is not supported in Postgres yet for any fields
|
||||||
|
mongoIt('should allow REST all querying on polymorphic relationships', async () => {
|
||||||
|
const movie = await payload.create({
|
||||||
|
collection: 'movies',
|
||||||
|
data: {
|
||||||
|
name: 'Pulp Fiction 2',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
await payload.create({
|
||||||
|
collection: polymorphicRelationshipsSlug,
|
||||||
|
data: {
|
||||||
|
polymorphic: {
|
||||||
|
relationTo: 'movies',
|
||||||
|
value: movie.id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const queryOne = await restClient
|
||||||
|
.GET(`/${polymorphicRelationshipsSlug}`, {
|
||||||
|
query: {
|
||||||
|
where: {
|
||||||
|
'polymorphic.value': {
|
||||||
|
all: [movie.id],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then((res) => res.json())
|
||||||
|
|
||||||
|
expect(queryOne.docs).toHaveLength(1)
|
||||||
|
})
|
||||||
|
|
||||||
it('should allow querying on polymorphic relationships with an object syntax', async () => {
|
it('should allow querying on polymorphic relationships with an object syntax', async () => {
|
||||||
const movie = await payload.create({
|
const movie = await payload.create({
|
||||||
collection: 'movies',
|
collection: 'movies',
|
||||||
|
|||||||
Reference in New Issue
Block a user