fix(db-postgres): joins with versions and hasMany relationship (#9370)

Fixes errors when having joins with versions +drafts on `hasMany: true`
relationships.
Removes `joinQuery` overhead if we don't need it for the current
operation. Right now, in all adapters we support joins only for `find`,
`findOne`, and `queryDrafts`.


Fixes https://github.com/payloadcms/payload/issues/9369
This commit is contained in:
Sasha
2024-11-21 17:42:05 +02:00
committed by GitHub
parent 3d0424bc77
commit d499de1e0f
11 changed files with 60 additions and 11 deletions

View File

@@ -19,6 +19,7 @@ export const deleteMany: DeleteMany = async function deleteMany(
const result = await findMany({
adapter: this,
fields: collectionConfig.fields,
joins: false,
limit: 0,
locale: req.locale,
page: 1,

View File

@@ -12,13 +12,7 @@ import { transform } from './transform/read/index.js'
export const deleteOne: DeleteOne = async function deleteOne(
this: DrizzleAdapter,
{
collection: collectionSlug,
joins: joinQuery,
req = {} as PayloadRequest,
select,
where: whereArg,
},
{ collection: collectionSlug, req = {} as PayloadRequest, select, where: whereArg },
) {
const db = this.sessions[await req?.transactionID]?.db || this.drizzle
const collection = this.payload.collections[collectionSlug].config
@@ -54,7 +48,7 @@ export const deleteOne: DeleteOne = async function deleteOne(
adapter: this,
depth: 0,
fields: collection.fields,
joinQuery,
joinQuery: false,
select,
tableName,
})
@@ -69,7 +63,7 @@ export const deleteOne: DeleteOne = async function deleteOne(
config: this.payload.config,
data: docToDelete,
fields: collection.fields,
joinQuery,
joinQuery: false,
})
await this.deleteWhere({

View File

@@ -24,6 +24,7 @@ export const deleteVersions: DeleteVersions = async function deleteVersion(
const { docs } = await findMany({
adapter: this,
fields,
joins: false,
limit: 0,
locale,
page: 1,

View File

@@ -330,7 +330,6 @@ export const traverseFields = ({
}
case 'group':
case 'tab': {
const fieldSelect = select?.[field.name]
@@ -364,6 +363,7 @@ export const traverseFields = ({
break
}
case 'join': {
// when `joinsQuery` is false, do not join
if (joinQuery === false) {

View File

@@ -34,6 +34,7 @@ export const findVersions: FindVersions = async function findVersions(
return findMany({
adapter: this,
fields,
joins: false,
limit,
locale,
page,

View File

@@ -49,6 +49,7 @@ export async function updateVersion<T extends TypeWithID>(
data: versionData,
db,
fields,
joinQuery: false,
operation: 'update',
req,
select,

View File

@@ -411,6 +411,8 @@ export const upsertRow = async <T extends Record<string, unknown> | TypeWithID>(
// RETRIEVE NEWLY UPDATED ROW
// //////////////////////////////////
joinQuery = operation === 'create' ? false : joinQuery
const findManyArgs = buildFindManyArgs({
adapter,
depth: 0,

View File

@@ -13,6 +13,12 @@ export const CategoriesVersions: CollectionConfig = {
collection: versionsSlug,
on: 'categoryVersion',
},
{
name: 'relatedVersionsMany',
type: 'join',
collection: versionsSlug,
on: 'categoryVersions',
},
],
versions: {
drafts: true,

View File

@@ -15,6 +15,12 @@ export const Versions: CollectionConfig = {
relationTo: 'categories-versions',
type: 'relationship',
},
{
name: 'categoryVersions',
relationTo: 'categories-versions',
type: 'relationship',
hasMany: true,
},
],
versions: {
drafts: true,

View File

@@ -480,7 +480,20 @@ describe('Joins Field', () => {
expect(res.docs[0].relatedVersions.docs[0].id).toBe(version.id)
})
it('should populate joins when versions on both sides draft true payload.db.queryDrafts', async () => {
it('should populate joins with hasMany relationships when versions on both sides draft false', async () => {
const category = await payload.create({ collection: 'categories-versions', data: {} })
const version = await payload.create({
collection: 'versions',
data: { categoryVersions: [category.id] },
})
const res = await payload.find({ collection: 'categories-versions', draft: false })
expect(res.docs[0].relatedVersionsMany.docs[0].id).toBe(version.id)
})
it('should populate joins with hasMany relationships when versions on both sides draft true payload.db.queryDrafts', async () => {
const category = await payload.create({ collection: 'categories-versions', data: {} })
const version = await payload.create({
@@ -495,6 +508,22 @@ describe('Joins Field', () => {
expect(res.docs[0].relatedVersions.docs[0].id).toBe(version.id)
})
it('should populate joins when versions on both sides draft true payload.db.queryDrafts', async () => {
const category = await payload.create({ collection: 'categories-versions', data: {} })
const version = await payload.create({
collection: 'versions',
data: { categoryVersions: [category.id] },
})
const res = await payload.find({
collection: 'categories-versions',
draft: true,
})
expect(res.docs[0].relatedVersionsMany.docs[0].id).toBe(version.id)
})
})
describe('REST', () => {

View File

@@ -41,6 +41,7 @@ export interface Config {
};
'categories-versions': {
relatedVersions: 'versions';
relatedVersionsMany: 'versions';
};
'localized-categories': {
relatedPosts: 'localized-posts';
@@ -197,6 +198,7 @@ export interface Version {
id: string;
category?: (string | null) | Category;
categoryVersion?: (string | null) | CategoriesVersion;
categoryVersions?: (string | CategoriesVersion)[] | null;
updatedAt: string;
createdAt: string;
_status?: ('draft' | 'published') | null;
@@ -211,6 +213,10 @@ export interface CategoriesVersion {
docs?: (string | Version)[] | null;
hasNextPage?: boolean | null;
} | null;
relatedVersionsMany?: {
docs?: (string | Version)[] | null;
hasNextPage?: boolean | null;
} | null;
updatedAt: string;
createdAt: string;
_status?: ('draft' | 'published') | null;
@@ -442,6 +448,7 @@ export interface UploadsSelect<T extends boolean = true> {
export interface VersionsSelect<T extends boolean = true> {
category?: T;
categoryVersion?: T;
categoryVersions?: T;
updatedAt?: T;
createdAt?: T;
_status?: T;
@@ -452,6 +459,7 @@ export interface VersionsSelect<T extends boolean = true> {
*/
export interface CategoriesVersionsSelect<T extends boolean = true> {
relatedVersions?: T;
relatedVersionsMany?: T;
updatedAt?: T;
createdAt?: T;
_status?: T;