Skip to content

Commit d601300

Browse files
authored
fix(db-mongodb): querying polymorphic relationships with the all operator (#10704)
Fixes #10678
1 parent e5b3da9 commit d601300

File tree

4 files changed

+106
-17
lines changed

4 files changed

+106
-17
lines changed

docs/queries/overview.mdx

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -39,23 +39,23 @@ _The exact query syntax will depend on the API you are using, but the concepts a
3939

4040
The following operators are available for use in queries:
4141

42-
| Operator | Description |
43-
| -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
44-
| `equals` | The value must be exactly equal. |
45-
| `not_equals` | The query will return all documents where the value is not equal. |
46-
| `greater_than` | For numeric or date-based fields. |
47-
| `greater_than_equal` | For numeric or date-based fields. |
48-
| `less_than` | For numeric or date-based fields. |
49-
| `less_than_equal` | For numeric or date-based fields. |
50-
| `like` | Case-insensitive string must be present. If string of words, all words must be present, in any order. |
51-
| `contains` | Must contain the value entered, case-insensitive. |
52-
| `in` | The value must be found within the provided comma-delimited list of values. |
53-
| `not_in` | The value must NOT be within the provided comma-delimited list of values. |
54-
| `all` | The value must contain all values provided in the comma-delimited list. |
55-
| `exists` | Only return documents where the value either exists (`true`) or does not exist (`false`). |
56-
| `near` | For distance related to a [Point Field](../fields/point) comma separated as `<longitude>, <latitude>, <maxDistance in meters (nullable)>, <minDistance in meters (nullable)>`. |
57-
| `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) |
58-
| `intersects` | For [Point Fields](../fields/point) to filter documents based on whether points intersect with the given area defined in GeoJSON. [Example](../fields/point#querying-intersects) |
42+
| Operator | Description |
43+
| -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
44+
| `equals` | The value must be exactly equal. |
45+
| `not_equals` | The query will return all documents where the value is not equal. |
46+
| `greater_than` | For numeric or date-based fields. |
47+
| `greater_than_equal` | For numeric or date-based fields. |
48+
| `less_than` | For numeric or date-based fields. |
49+
| `less_than_equal` | For numeric or date-based fields. |
50+
| `like` | Case-insensitive string must be present. If string of words, all words must be present, in any order. |
51+
| `contains` | Must contain the value entered, case-insensitive. |
52+
| `in` | The value must be found within the provided comma-delimited list of values. |
53+
| `not_in` | The value must NOT be within the provided comma-delimited list of values. |
54+
| `all` | The value must contain all values provided in the comma-delimited list. Note: currently this operator is supported only with the MongoDB adapter. |
55+
| `exists` | Only return documents where the value either exists (`true`) or does not exist (`false`). |
56+
| `near` | For distance related to a [Point Field](../fields/point) comma separated as `<longitude>, <latitude>, <maxDistance in meters (nullable)>, <minDistance in meters (nullable)>`. |
57+
| `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) |
58+
| `intersects` | For [Point Fields](../fields/point) to filter documents based on whether points intersect with the given area defined in GeoJSON. [Example](../fields/point#querying-intersects) |
5959

6060
<Banner type="success">
6161
**Tip:**

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,19 @@ export const sanitizeQueryValue = ({
324324
}
325325
}
326326
}
327+
328+
if (
329+
operator === 'all' &&
330+
Array.isArray(relationTo) &&
331+
path.endsWith('.value') &&
332+
Array.isArray(formattedValue)
333+
) {
334+
formattedValue.forEach((v, i) => {
335+
if (Types.ObjectId.isValid(v)) {
336+
formattedValue[i] = new Types.ObjectId(v)
337+
}
338+
})
339+
}
327340
}
328341

329342
// Set up specific formatting necessary by operators

packages/drizzle/src/queries/operatorMap.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ export const operatorMap: Operators = {
4848
less_than_equal: lte,
4949
like: ilike,
5050
not_equals: ne,
51+
// TODO: support this
5152
// all: all,
5253
not_in: notInArray,
5354
or,

test/relationships/int.spec.ts

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ const dirname = path.dirname(filename)
3939

4040
type EasierChained = { id: string; relation: EasierChained }
4141

42+
const mongoIt = process.env.PAYLOAD_DATABASE === 'mongodb' ? it : it.skip
43+
4244
describe('Relationships', () => {
4345
beforeAll(async () => {
4446
;({ payload, restClient } = await initPayloadInt(dirname))
@@ -459,6 +461,46 @@ describe('Relationships', () => {
459461
expect(query2.totalDocs).toStrictEqual(2)
460462
})
461463

464+
// all operator is not supported in Postgres yet for any fields
465+
mongoIt('should query using "all" by hasMany relationship field', async () => {
466+
const movie1 = await payload.create({
467+
collection: 'movies',
468+
data: {},
469+
})
470+
const movie2 = await payload.create({
471+
collection: 'movies',
472+
data: {},
473+
})
474+
475+
await payload.create({
476+
collection: 'directors',
477+
data: {
478+
name: 'Quentin Tarantino',
479+
movies: [movie2.id, movie1.id],
480+
},
481+
})
482+
483+
await payload.create({
484+
collection: 'directors',
485+
data: {
486+
name: 'Quentin Tarantino',
487+
movies: [movie2.id],
488+
},
489+
})
490+
491+
const query1 = await payload.find({
492+
collection: 'directors',
493+
depth: 0,
494+
where: {
495+
movies: {
496+
all: [movie1.id],
497+
},
498+
},
499+
})
500+
501+
expect(query1.totalDocs).toStrictEqual(1)
502+
})
503+
462504
it('should sort by a property of a hasMany relationship', async () => {
463505
// no support for sort by relation in mongodb
464506
if (isMongoose(payload)) {
@@ -1352,6 +1394,39 @@ describe('Relationships', () => {
13521394
expect(queryTwo.docs).toHaveLength(1)
13531395
})
13541396

1397+
// all operator is not supported in Postgres yet for any fields
1398+
mongoIt('should allow REST all querying on polymorphic relationships', async () => {
1399+
const movie = await payload.create({
1400+
collection: 'movies',
1401+
data: {
1402+
name: 'Pulp Fiction 2',
1403+
},
1404+
})
1405+
await payload.create({
1406+
collection: polymorphicRelationshipsSlug,
1407+
data: {
1408+
polymorphic: {
1409+
relationTo: 'movies',
1410+
value: movie.id,
1411+
},
1412+
},
1413+
})
1414+
1415+
const queryOne = await restClient
1416+
.GET(`/${polymorphicRelationshipsSlug}`, {
1417+
query: {
1418+
where: {
1419+
'polymorphic.value': {
1420+
all: [movie.id],
1421+
},
1422+
},
1423+
},
1424+
})
1425+
.then((res) => res.json())
1426+
1427+
expect(queryOne.docs).toHaveLength(1)
1428+
})
1429+
13551430
it('should allow querying on polymorphic relationships with an object syntax', async () => {
13561431
const movie = await payload.create({
13571432
collection: 'movies',

0 commit comments

Comments
 (0)