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`.
This commit is contained in:
Sasha
2024-10-30 18:06:03 +02:00
committed by GitHub
parent 123125185c
commit f4041ce6e2
6 changed files with 62 additions and 3 deletions

View File

@@ -106,7 +106,7 @@ export const buildJoinAggregation = async ({
$lookup: { $lookup: {
as: `${as}.docs`, as: `${as}.docs`,
foreignField: `${join.field.on}${code}`, foreignField: `${join.field.on}${code}`,
from: slug, from: adapter.collections[slug].collection.name,
localField: versions ? 'parent' : '_id', localField: versions ? 'parent' : '_id',
pipeline, pipeline,
}, },
@@ -147,7 +147,7 @@ export const buildJoinAggregation = async ({
$lookup: { $lookup: {
as: `${as}.docs`, as: `${as}.docs`,
foreignField: `${join.field.on}${localeSuffix}`, foreignField: `${join.field.on}${localeSuffix}`,
from: slug, from: adapter.collections[slug].collection.name,
localField: versions ? 'parent' : '_id', localField: versions ? 'parent' : '_id',
pipeline, pipeline,
}, },

View File

@@ -1,6 +1,7 @@
import type { CollectionConfig } from 'payload' import type { CollectionConfig } from 'payload'
import { categoriesSlug, postsSlug } from '../shared.js' import { categoriesSlug, postsSlug } from '../shared.js'
import { singularSlug } from './Singular.js'
export const Categories: CollectionConfig = { export const Categories: CollectionConfig = {
slug: categoriesSlug, slug: categoriesSlug,
@@ -83,5 +84,11 @@ export const Categories: CollectionConfig = {
}, },
], ],
}, },
{
name: 'singulars',
type: 'join',
collection: singularSlug,
on: 'category',
},
], ],
} }

View File

@@ -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',
},
],
}

View File

@@ -5,6 +5,7 @@ import { buildConfigWithDefaults } from '../buildConfigWithDefaults.js'
import { Categories } from './collections/Categories.js' import { Categories } from './collections/Categories.js'
import { CategoriesVersions } from './collections/CategoriesVersions.js' import { CategoriesVersions } from './collections/CategoriesVersions.js'
import { Posts } from './collections/Posts.js' import { Posts } from './collections/Posts.js'
import { Singular } from './collections/Singular.js'
import { Uploads } from './collections/Uploads.js' import { Uploads } from './collections/Uploads.js'
import { Versions } from './collections/Versions.js' import { Versions } from './collections/Versions.js'
import { seed } from './seed.js' import { seed } from './seed.js'
@@ -20,6 +21,7 @@ export default buildConfigWithDefaults({
Uploads, Uploads,
Versions, Versions,
CategoriesVersions, CategoriesVersions,
Singular,
{ {
slug: localizedPostsSlug, slug: localizedPostsSlug,
admin: { admin: {

View File

@@ -5,7 +5,7 @@ import { getFileByPath } from 'payload'
import { fileURLToPath } from 'url' import { fileURLToPath } from 'url'
import type { NextRESTClient } from '../helpers/NextRESTClient.js' 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 { devUser } from '../credentials.js'
import { idToString } from '../helpers/idToString.js' import { idToString } from '../helpers/idToString.js'
@@ -642,6 +642,8 @@ describe('Joins Field', () => {
} }
} }
}` }`
expect(true).toBeTruthy()
const response = await restClient const response = await restClient
.GRAPHQL_POST({ body: JSON.stringify({ query }) }) .GRAPHQL_POST({ body: JSON.stringify({ query }) })
.then((res) => res.json()) .then((res) => res.json())
@@ -666,6 +668,21 @@ describe('Joins Field', () => {
expect(allCategories.totalDocs).toBe(allCategoriesByIds.totalDocs) 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<Post>, locale?: Config['locale']) { async function createPost(overrides?: Partial<Post>, locale?: Config['locale']) {

View File

@@ -16,6 +16,7 @@ export interface Config {
uploads: Upload; uploads: Upload;
versions: Version; versions: Version;
'categories-versions': CategoriesVersion; 'categories-versions': CategoriesVersion;
singular: Singular;
'localized-posts': LocalizedPost; 'localized-posts': LocalizedPost;
'localized-categories': LocalizedCategory; 'localized-categories': LocalizedCategory;
users: User; users: User;
@@ -133,6 +134,20 @@ export interface Category {
hasNextPage?: boolean | null; hasNextPage?: boolean | null;
} | 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; updatedAt: string;
createdAt: string; createdAt: string;
} }
@@ -231,6 +246,10 @@ export interface PayloadLockedDocument {
relationTo: 'categories-versions'; relationTo: 'categories-versions';
value: string | CategoriesVersion; value: string | CategoriesVersion;
} | null) } | null)
| ({
relationTo: 'singular';
value: string | Singular;
} | null)
| ({ | ({
relationTo: 'localized-posts'; relationTo: 'localized-posts';
value: string | LocalizedPost; value: string | LocalizedPost;