From f4041ce6e29cb7f3443642b82a9c70433e893b5c Mon Sep 17 00:00:00 2001 From: Sasha <64744993+r1tsuu@users.noreply.github.com> Date: Wed, 30 Oct 2024 18:06:03 +0200 Subject: [PATCH] fix(db-mongodb): joins with singular collection name (#8933) ### What? Properly specifies `$lookup.from` when the collection name is singular. ### Why? MongoDB can pluralize the collection name and so can be different for singular ones. ### How? Uses the collection name from the driver directly `adapter.collections[slug].collection.name` instead of just `slug`. --- .../src/utilities/buildJoinAggregation.ts | 4 ++-- test/joins/collections/Categories.ts | 7 +++++++ test/joins/collections/Singular.ts | 14 ++++++++++++++ test/joins/config.ts | 2 ++ test/joins/int.spec.ts | 19 ++++++++++++++++++- test/joins/payload-types.ts | 19 +++++++++++++++++++ 6 files changed, 62 insertions(+), 3 deletions(-) create mode 100644 test/joins/collections/Singular.ts diff --git a/packages/db-mongodb/src/utilities/buildJoinAggregation.ts b/packages/db-mongodb/src/utilities/buildJoinAggregation.ts index ab0654c596..99065d2527 100644 --- a/packages/db-mongodb/src/utilities/buildJoinAggregation.ts +++ b/packages/db-mongodb/src/utilities/buildJoinAggregation.ts @@ -106,7 +106,7 @@ export const buildJoinAggregation = async ({ $lookup: { as: `${as}.docs`, foreignField: `${join.field.on}${code}`, - from: slug, + from: adapter.collections[slug].collection.name, localField: versions ? 'parent' : '_id', pipeline, }, @@ -147,7 +147,7 @@ export const buildJoinAggregation = async ({ $lookup: { as: `${as}.docs`, foreignField: `${join.field.on}${localeSuffix}`, - from: slug, + from: adapter.collections[slug].collection.name, localField: versions ? 'parent' : '_id', pipeline, }, diff --git a/test/joins/collections/Categories.ts b/test/joins/collections/Categories.ts index 6604c13ed4..45ae14a075 100644 --- a/test/joins/collections/Categories.ts +++ b/test/joins/collections/Categories.ts @@ -1,6 +1,7 @@ import type { CollectionConfig } from 'payload' import { categoriesSlug, postsSlug } from '../shared.js' +import { singularSlug } from './Singular.js' export const Categories: CollectionConfig = { slug: categoriesSlug, @@ -83,5 +84,11 @@ export const Categories: CollectionConfig = { }, ], }, + { + name: 'singulars', + type: 'join', + collection: singularSlug, + on: 'category', + }, ], } diff --git a/test/joins/collections/Singular.ts b/test/joins/collections/Singular.ts new file mode 100644 index 0000000000..e137f09a21 --- /dev/null +++ b/test/joins/collections/Singular.ts @@ -0,0 +1,14 @@ +import type { CollectionConfig } from 'payload' + +export const singularSlug = 'singular' + +export const Singular: CollectionConfig = { + slug: singularSlug, + fields: [ + { + type: 'relationship', + relationTo: 'categories', + name: 'category', + }, + ], +} diff --git a/test/joins/config.ts b/test/joins/config.ts index 8d56b04e5f..1c7c70c186 100644 --- a/test/joins/config.ts +++ b/test/joins/config.ts @@ -5,6 +5,7 @@ import { buildConfigWithDefaults } from '../buildConfigWithDefaults.js' import { Categories } from './collections/Categories.js' import { CategoriesVersions } from './collections/CategoriesVersions.js' import { Posts } from './collections/Posts.js' +import { Singular } from './collections/Singular.js' import { Uploads } from './collections/Uploads.js' import { Versions } from './collections/Versions.js' import { seed } from './seed.js' @@ -20,6 +21,7 @@ export default buildConfigWithDefaults({ Uploads, Versions, CategoriesVersions, + Singular, { slug: localizedPostsSlug, admin: { diff --git a/test/joins/int.spec.ts b/test/joins/int.spec.ts index 10a6852a93..1f39896a14 100644 --- a/test/joins/int.spec.ts +++ b/test/joins/int.spec.ts @@ -5,7 +5,7 @@ import { getFileByPath } from 'payload' import { fileURLToPath } from 'url' import type { NextRESTClient } from '../helpers/NextRESTClient.js' -import type { Category, Config, Post } from './payload-types.js' +import type { Category, Config, Post, Singular } from './payload-types.js' import { devUser } from '../credentials.js' import { idToString } from '../helpers/idToString.js' @@ -642,6 +642,8 @@ describe('Joins Field', () => { } } }` + + expect(true).toBeTruthy() const response = await restClient .GRAPHQL_POST({ body: JSON.stringify({ query }) }) .then((res) => res.json()) @@ -666,6 +668,21 @@ describe('Joins Field', () => { expect(allCategories.totalDocs).toBe(allCategoriesByIds.totalDocs) }) + + it('should join with singular collection name', async () => { + const { + docs: [category], + } = await payload.find({ collection: 'categories', limit: 1, depth: 0 }) + + const singular = await payload.create({ + collection: 'singular', + data: { category: category.id }, + }) + + const categoryWithJoins = await payload.findByID({ collection: 'categories', id: category.id }) + + expect((categoryWithJoins.singulars.docs[0] as Singular).id).toBe(singular.id) + }) }) async function createPost(overrides?: Partial, locale?: Config['locale']) { diff --git a/test/joins/payload-types.ts b/test/joins/payload-types.ts index f388f2472d..d05931e881 100644 --- a/test/joins/payload-types.ts +++ b/test/joins/payload-types.ts @@ -16,6 +16,7 @@ export interface Config { uploads: Upload; versions: Version; 'categories-versions': CategoriesVersion; + singular: Singular; 'localized-posts': LocalizedPost; 'localized-categories': LocalizedCategory; users: User; @@ -133,6 +134,20 @@ export interface Category { hasNextPage?: boolean | null; } | null; }; + singulars?: { + docs?: (string | Singular)[] | null; + hasNextPage?: boolean | null; + } | null; + updatedAt: string; + createdAt: string; +} +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "singular". + */ +export interface Singular { + id: string; + category?: (string | null) | Category; updatedAt: string; createdAt: string; } @@ -231,6 +246,10 @@ export interface PayloadLockedDocument { relationTo: 'categories-versions'; value: string | CategoriesVersion; } | null) + | ({ + relationTo: 'singular'; + value: string | Singular; + } | null) | ({ relationTo: 'localized-posts'; value: string | LocalizedPost;