fix(db-postgres): joins to self collection (#10182)

### What?
With Postgres, before join to self like:
```ts
import type { CollectionConfig } from 'payload'

export const SelfJoins: CollectionConfig = {
  slug: 'self-joins',
  fields: [
    {
      name: 'rel',
      type: 'relationship',
      relationTo: 'self-joins',
    },
    {
      name: 'joins',
      type: 'join',
      on: 'rel',
      collection: 'self-joins',
    },
  ],
}
```
wasn't possible, even though it's a valid usage.

### How?
Now, to differentiate parent `self_joins` and children `self_joins` we
do additional alias for the nested select -
`"4d3cf2b6_1adf_46a8_b6d2_3e1c3809d737"`:
```sql
select 
  "id", 
  "rel_id", 
  "updated_at", 
  "created_at", 
  (
    select 
      coalesce(
        json_agg(
          json_build_object('id', "joins_alias".id)
        ), 
        '[]' :: json
      ) 
    from 
      (
        select 
          "created_at", 
          "rel_id", 
          "id" 
        from 
          "self_joins" "4d3cf2b6_1adf_46a8_b6d2_3e1c3809d737" 
        where 
          "4d3cf2b6_1adf_46a8_b6d2_3e1c3809d737"."rel_id" = "self_joins"."id" 
        order by 
          "4d3cf2b6_1adf_46a8_b6d2_3e1c3809d737"."created_at" desc 
        limit 
          $1
      ) "joins_alias"
  ) as "joins_alias" 
from 
  "self_joins" 
where 
  "self_joins"."id" = $2 
order by 
  "self_joins"."created_at" desc 
limit 
  $3

```

Fixes https://github.com/payloadcms/payload/issues/10144

-->
This commit is contained in:
Sasha
2024-12-26 20:47:49 +02:00
committed by GitHub
parent 6b45b2d7e9
commit a0d8131649
9 changed files with 119 additions and 16 deletions

View File

@@ -0,0 +1,18 @@
import type { CollectionConfig } from 'payload'
export const SelfJoins: CollectionConfig = {
slug: 'self-joins',
fields: [
{
name: 'rel',
type: 'relationship',
relationTo: 'self-joins',
},
{
name: 'joins',
type: 'join',
on: 'rel',
collection: 'self-joins',
},
],
}

View File

@@ -6,6 +6,7 @@ import { Categories } from './collections/Categories.js'
import { CategoriesVersions } from './collections/CategoriesVersions.js'
import { HiddenPosts } from './collections/HiddenPosts.js'
import { Posts } from './collections/Posts.js'
import { SelfJoins } from './collections/SelfJoins.js'
import { Singular } from './collections/Singular.js'
import { Uploads } from './collections/Uploads.js'
import { Versions } from './collections/Versions.js'
@@ -37,6 +38,7 @@ export default buildConfigWithDefaults({
Versions,
CategoriesVersions,
Singular,
SelfJoins,
{
slug: localizedPostsSlug,
admin: {

View File

@@ -1,4 +1,4 @@
import type { Payload } from 'payload'
import type { Payload, TypeWithID } from 'payload'
import path from 'path'
import { getFileByPath } from 'payload'
@@ -975,6 +975,15 @@ describe('Joins Field', () => {
await payload.delete({ collection: categoriesSlug, where: { name: { equals: 'totalDocs' } } })
})
it('should self join', async () => {
const doc_1 = await payload.create({ collection: 'self-joins', data: {} })
const doc_2 = await payload.create({ collection: 'self-joins', data: { rel: doc_1 }, depth: 0 })
const data = await payload.findByID({ collection: 'self-joins', id: doc_1.id, depth: 1 })
expect((data.joins.docs[0] as TypeWithID).id).toBe(doc_2.id)
})
})
async function createPost(overrides?: Partial<Post>, locale?: Config['locale']) {

View File

@@ -18,6 +18,7 @@ export interface Config {
versions: Version;
'categories-versions': CategoriesVersion;
singular: Singular;
'self-joins': SelfJoin;
'localized-posts': LocalizedPost;
'localized-categories': LocalizedCategory;
'restricted-categories': RestrictedCategory;
@@ -53,6 +54,9 @@ export interface Config {
relatedVersions: 'versions';
relatedVersionsMany: 'versions';
};
'self-joins': {
joins: 'self-joins';
};
'localized-categories': {
relatedPosts: 'localized-posts';
};
@@ -71,6 +75,7 @@ export interface Config {
versions: VersionsSelect<false> | VersionsSelect<true>;
'categories-versions': CategoriesVersionsSelect<false> | CategoriesVersionsSelect<true>;
singular: SingularSelect<false> | SingularSelect<true>;
'self-joins': SelfJoinsSelect<false> | SelfJoinsSelect<true>;
'localized-posts': LocalizedPostsSelect<false> | LocalizedPostsSelect<true>;
'localized-categories': LocalizedCategoriesSelect<false> | LocalizedCategoriesSelect<true>;
'restricted-categories': RestrictedCategoriesSelect<false> | RestrictedCategoriesSelect<true>;
@@ -355,6 +360,20 @@ export interface CategoriesVersion {
createdAt: string;
_status?: ('draft' | 'published') | null;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "self-joins".
*/
export interface SelfJoin {
id: string;
rel?: (string | null) | SelfJoin;
joins?: {
docs?: (string | SelfJoin)[] | null;
hasNextPage?: boolean | null;
} | null;
updatedAt: string;
createdAt: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "localized-posts".
@@ -467,6 +486,10 @@ export interface PayloadLockedDocument {
relationTo: 'singular';
value: string | Singular;
} | null)
| ({
relationTo: 'self-joins';
value: string | SelfJoin;
} | null)
| ({
relationTo: 'localized-posts';
value: string | LocalizedPost;
@@ -666,6 +689,16 @@ export interface SingularSelect<T extends boolean = true> {
updatedAt?: T;
createdAt?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "self-joins_select".
*/
export interface SelfJoinsSelect<T extends boolean = true> {
rel?: T;
joins?: T;
updatedAt?: T;
createdAt?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "localized-posts_select".