fix(db-postgres): select query on upload fields with hasMany: true (#10029)

Previously, if you selected only upload `hasMany: true` field, you would
receive an empty arrays always, because the `_rels` table wasn't joined
in this case. Fixes the condition to count `field.type === 'upload'` .
This commit is contained in:
Sasha
2024-12-18 06:33:12 +02:00
committed by GitHub
parent 61c5e0d3e0
commit 2ee3e30b50
6 changed files with 244 additions and 2 deletions

View File

@@ -526,7 +526,7 @@ export const traverseFields = ({
if ( if (
!withTabledFields.rels && !withTabledFields.rels &&
field.type === 'relationship' && (field.type === 'relationship' || field.type === 'upload') &&
(field.hasMany || Array.isArray(field.relationTo)) (field.hasMany || Array.isArray(field.relationTo))
) { ) {
withTabledFields.rels = true withTabledFields.rels = true

View File

@@ -116,5 +116,33 @@ export const PostsCollection: CollectionConfig = {
}, },
], ],
}, },
{
type: 'relationship',
name: 'hasOne',
relationTo: 'rels',
},
{
type: 'relationship',
name: 'hasMany',
hasMany: true,
relationTo: 'rels',
},
{
type: 'upload',
name: 'hasManyUpload',
hasMany: true,
relationTo: 'upload',
},
{
type: 'relationship',
name: 'hasOnePoly',
relationTo: ['rels'],
},
{
type: 'relationship',
name: 'hasManyPoly',
hasMany: true,
relationTo: ['rels'],
},
], ],
} }

View File

@@ -23,6 +23,17 @@ export default buildConfigWithDefaults({
DeepPostsCollection, DeepPostsCollection,
Pages, Pages,
Points, Points,
{
slug: 'upload',
fields: [],
upload: {
staticDir: path.resolve(dirname, 'media'),
},
},
{
slug: 'rels',
fields: [],
},
], ],
globals: [ globals: [
{ {

BIN
test/select/image.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

View File

@@ -64,6 +64,7 @@ describe('Select', () => {
collection: 'posts', collection: 'posts',
id: postId, id: postId,
select: {}, select: {},
depth: 0,
}) })
expect(res).toStrictEqual({ expect(res).toStrictEqual({
@@ -78,6 +79,7 @@ describe('Select', () => {
select: { select: {
number: true, number: true,
}, },
depth: 0,
}) })
expect(res).toStrictEqual({ expect(res).toStrictEqual({
@@ -93,6 +95,7 @@ describe('Select', () => {
select: { select: {
select: true, select: true,
}, },
depth: 0,
}) })
expect(res).toStrictEqual({ expect(res).toStrictEqual({
@@ -108,6 +111,7 @@ describe('Select', () => {
select: { select: {
selectMany: true, selectMany: true,
}, },
depth: 0,
}) })
expect(res).toStrictEqual({ expect(res).toStrictEqual({
@@ -124,6 +128,7 @@ describe('Select', () => {
number: true, number: true,
text: true, text: true,
}, },
depth: 0,
}) })
expect(res).toStrictEqual({ expect(res).toStrictEqual({
@@ -133,6 +138,50 @@ describe('Select', () => {
}) })
}) })
it('should select relationships', async () => {
const res_1 = await payload.findByID({
collection: 'posts',
id: postId,
select: {
hasManyUpload: true,
},
depth: 0,
})
expect(res_1).toStrictEqual({
id: postId,
hasManyUpload: post.hasManyUpload,
})
const res_2 = await payload.findByID({
collection: 'posts',
id: postId,
select: {
hasOne: true,
},
depth: 0,
})
expect(res_2).toStrictEqual({
id: postId,
hasOne: post.hasOne,
})
const res_3 = await payload.findByID({
collection: 'posts',
id: postId,
select: {
hasManyPoly: true,
},
depth: 0,
})
expect(res_3).toStrictEqual({
id: postId,
hasManyPoly: post.hasManyPoly,
})
})
it('should select all the fields inside of group', async () => { it('should select all the fields inside of group', async () => {
const res = await payload.findByID({ const res = await payload.findByID({
collection: 'posts', collection: 'posts',
@@ -140,6 +189,7 @@ describe('Select', () => {
select: { select: {
group: true, group: true,
}, },
depth: 0,
}) })
expect(res).toStrictEqual({ expect(res).toStrictEqual({
@@ -157,6 +207,7 @@ describe('Select', () => {
text: true, text: true,
}, },
}, },
depth: 0,
}) })
expect(res).toStrictEqual({ expect(res).toStrictEqual({
@@ -174,6 +225,7 @@ describe('Select', () => {
select: { select: {
tab: true, tab: true,
}, },
depth: 0,
}) })
expect(res).toStrictEqual({ expect(res).toStrictEqual({
@@ -191,6 +243,7 @@ describe('Select', () => {
text: true, text: true,
}, },
}, },
depth: 0,
}) })
expect(res).toStrictEqual({ expect(res).toStrictEqual({
@@ -208,6 +261,7 @@ describe('Select', () => {
select: { select: {
unnamedTabText: true, unnamedTabText: true,
}, },
depth: 0,
}) })
expect(res).toStrictEqual({ expect(res).toStrictEqual({
@@ -223,6 +277,7 @@ describe('Select', () => {
select: { select: {
array: {}, array: {},
}, },
depth: 0,
}) })
expect(res).toStrictEqual({ expect(res).toStrictEqual({
@@ -238,6 +293,7 @@ describe('Select', () => {
select: { select: {
array: true, array: true,
}, },
depth: 0,
}) })
expect(res).toStrictEqual({ expect(res).toStrictEqual({
@@ -255,6 +311,7 @@ describe('Select', () => {
text: true, text: true,
}, },
}, },
depth: 0,
}) })
expect(res).toStrictEqual({ expect(res).toStrictEqual({
@@ -273,6 +330,7 @@ describe('Select', () => {
select: { select: {
blocks: {}, blocks: {},
}, },
depth: 0,
}) })
expect(res).toStrictEqual({ expect(res).toStrictEqual({
@@ -288,6 +346,7 @@ describe('Select', () => {
select: { select: {
blocks: true, blocks: true,
}, },
depth: 0,
}) })
expect(res).toStrictEqual({ expect(res).toStrictEqual({
@@ -305,6 +364,7 @@ describe('Select', () => {
cta: true, cta: true,
}, },
}, },
depth: 0,
}) })
expect(res).toStrictEqual({ expect(res).toStrictEqual({
@@ -330,6 +390,7 @@ describe('Select', () => {
cta: { ctaText: true }, cta: { ctaText: true },
}, },
}, },
depth: 0,
}) })
expect(res).toStrictEqual({ expect(res).toStrictEqual({
@@ -372,6 +433,7 @@ describe('Select', () => {
select: { select: {
text: false, text: false,
}, },
depth: 0,
}) })
const expected = { ...post } const expected = { ...post }
@@ -388,6 +450,7 @@ describe('Select', () => {
select: { select: {
number: false, number: false,
}, },
depth: 0,
}) })
const expected = { ...post } const expected = { ...post }
@@ -404,6 +467,7 @@ describe('Select', () => {
select: { select: {
select: false, select: false,
}, },
depth: 0,
}) })
const expected = { ...post } const expected = { ...post }
@@ -420,6 +484,7 @@ describe('Select', () => {
select: { select: {
selectMany: false, selectMany: false,
}, },
depth: 0,
}) })
const expected = { ...post } const expected = { ...post }
@@ -437,6 +502,7 @@ describe('Select', () => {
number: false, number: false,
text: false, text: false,
}, },
depth: 0,
}) })
const expected = { ...post } const expected = { ...post }
@@ -447,6 +513,29 @@ describe('Select', () => {
expect(res).toStrictEqual(expected) expect(res).toStrictEqual(expected)
}) })
it('should exclude relationships', async () => {
const res = await payload.findByID({
collection: 'posts',
id: postId,
select: {
hasOne: false,
hasMany: false,
hasManyPoly: false,
hasOnePoly: false,
},
depth: 0,
})
const expected = { ...post }
delete expected['hasOne']
delete expected['hasMany']
delete expected['hasManyPoly']
delete expected['hasOnePoly']
expect(res).toStrictEqual(expected)
})
it('should exclude group', async () => { it('should exclude group', async () => {
const res = await payload.findByID({ const res = await payload.findByID({
collection: 'posts', collection: 'posts',
@@ -454,6 +543,7 @@ describe('Select', () => {
select: { select: {
group: false, group: false,
}, },
depth: 0,
}) })
const expected = { ...post } const expected = { ...post }
@@ -472,6 +562,7 @@ describe('Select', () => {
text: false, text: false,
}, },
}, },
depth: 0,
}) })
const expected = deepCopyObject(post) const expected = deepCopyObject(post)
@@ -488,6 +579,7 @@ describe('Select', () => {
select: { select: {
array: false, array: false,
}, },
depth: 0,
}) })
const expected = { ...post } const expected = { ...post }
@@ -506,6 +598,7 @@ describe('Select', () => {
text: false, text: false,
}, },
}, },
depth: 0,
}) })
expect(res).toStrictEqual({ expect(res).toStrictEqual({
@@ -524,6 +617,7 @@ describe('Select', () => {
select: { select: {
blocks: false, blocks: false,
}, },
depth: 0,
}) })
const expected = { ...post } const expected = { ...post }
@@ -542,6 +636,7 @@ describe('Select', () => {
cta: false, cta: false,
}, },
}, },
depth: 0,
}) })
expect(res).toStrictEqual({ expect(res).toStrictEqual({
@@ -562,6 +657,7 @@ describe('Select', () => {
cta: { ctaText: false }, cta: { ctaText: false },
}, },
}, },
depth: 0,
}) })
expect(res).toStrictEqual({ expect(res).toStrictEqual({
@@ -1630,6 +1726,7 @@ describe('Select', () => {
const res = await restClient const res = await restClient
.GET(`/posts/${postId}`, { .GET(`/posts/${postId}`, {
query: { query: {
depth: 0,
select: { select: {
text: true, text: true,
} satisfies Config['collectionsSelect']['posts'], } satisfies Config['collectionsSelect']['posts'],
@@ -1647,6 +1744,7 @@ describe('Select', () => {
const res = await restClient const res = await restClient
.GET(`/posts/${postId}`, { .GET(`/posts/${postId}`, {
query: { query: {
depth: 0,
select: { select: {
number: true, number: true,
text: true, text: true,
@@ -1666,6 +1764,7 @@ describe('Select', () => {
const res = await restClient const res = await restClient
.GET(`/posts/${postId}`, { .GET(`/posts/${postId}`, {
query: { query: {
depth: 0,
select: { select: {
group: true, group: true,
} satisfies Config['collectionsSelect']['posts'], } satisfies Config['collectionsSelect']['posts'],
@@ -1683,6 +1782,7 @@ describe('Select', () => {
const res = await restClient const res = await restClient
.GET(`/posts/${postId}`, { .GET(`/posts/${postId}`, {
query: { query: {
depth: 0,
select: { select: {
group: { text: true }, group: { text: true },
} satisfies Config['collectionsSelect']['posts'], } satisfies Config['collectionsSelect']['posts'],
@@ -1704,6 +1804,7 @@ describe('Select', () => {
const res = await restClient const res = await restClient
.GET(`/posts/${postId}`, { .GET(`/posts/${postId}`, {
query: { query: {
depth: 0,
select: { select: {
text: false, text: false,
} satisfies Config['collectionsSelect']['posts'], } satisfies Config['collectionsSelect']['posts'],
@@ -1722,6 +1823,7 @@ describe('Select', () => {
const res = await restClient const res = await restClient
.GET(`/posts/${postId}`, { .GET(`/posts/${postId}`, {
query: { query: {
depth: 0,
select: { select: {
number: false, number: false,
} satisfies Config['collectionsSelect']['posts'], } satisfies Config['collectionsSelect']['posts'],
@@ -1740,6 +1842,7 @@ describe('Select', () => {
const res = await restClient const res = await restClient
.GET(`/posts/${postId}`, { .GET(`/posts/${postId}`, {
query: { query: {
depth: 0,
select: { select: {
number: false, number: false,
text: false, text: false,
@@ -1760,6 +1863,7 @@ describe('Select', () => {
const res = await restClient const res = await restClient
.GET(`/posts/${postId}`, { .GET(`/posts/${postId}`, {
query: { query: {
depth: 0,
select: { select: {
group: { group: {
text: false, text: false,
@@ -2129,7 +2233,19 @@ describe('Select', () => {
}) })
}) })
function createPost() { async function createPost() {
const upload = await payload.create({
collection: 'upload',
data: {},
filePath: path.resolve(dirname, 'image.jpg'),
})
const relation = await payload.create({
depth: 0,
collection: 'rels',
data: {},
})
return payload.create({ return payload.create({
collection: 'posts', collection: 'posts',
depth: 0, depth: 0,
@@ -2142,6 +2258,11 @@ function createPost() {
number: 1, number: 1,
text: 'text', text: 'text',
}, },
hasMany: [relation],
hasManyUpload: [upload],
hasOne: relation,
hasManyPoly: [{ relationTo: 'rels', value: relation }],
hasOnePoly: { relationTo: 'rels', value: relation },
blocks: [ blocks: [
{ {
blockType: 'cta', blockType: 'cta',

View File

@@ -17,6 +17,8 @@ export interface Config {
'deep-posts': DeepPost; 'deep-posts': DeepPost;
pages: Page; pages: Page;
points: Point; points: Point;
upload: Upload;
rels: Rel;
users: User; users: User;
'payload-locked-documents': PayloadLockedDocument; 'payload-locked-documents': PayloadLockedDocument;
'payload-preferences': PayloadPreference; 'payload-preferences': PayloadPreference;
@@ -30,6 +32,8 @@ export interface Config {
'deep-posts': DeepPostsSelect<false> | DeepPostsSelect<true>; 'deep-posts': DeepPostsSelect<false> | DeepPostsSelect<true>;
pages: PagesSelect<false> | PagesSelect<true>; pages: PagesSelect<false> | PagesSelect<true>;
points: PointsSelect<false> | PointsSelect<true>; points: PointsSelect<false> | PointsSelect<true>;
upload: UploadSelect<false> | UploadSelect<true>;
rels: RelsSelect<false> | RelsSelect<true>;
users: UsersSelect<false> | UsersSelect<true>; users: UsersSelect<false> | UsersSelect<true>;
'payload-locked-documents': PayloadLockedDocumentsSelect<false> | PayloadLockedDocumentsSelect<true>; 'payload-locked-documents': PayloadLockedDocumentsSelect<false> | PayloadLockedDocumentsSelect<true>;
'payload-preferences': PayloadPreferencesSelect<false> | PayloadPreferencesSelect<true>; 'payload-preferences': PayloadPreferencesSelect<false> | PayloadPreferencesSelect<true>;
@@ -116,6 +120,28 @@ export interface Post {
}; };
unnamedTabText?: string | null; unnamedTabText?: string | null;
unnamedTabNumber?: number | null; unnamedTabNumber?: number | null;
hasOne?: (string | null) | Rel;
hasMany?: (string | Rel)[] | null;
hasManyUpload?: (string | Rel)[] | null;
hasOnePoly?: {
relationTo: 'rels';
value: string | Rel;
} | null;
hasManyPoly?:
| {
relationTo: 'rels';
value: string | Rel;
}[]
| null;
updatedAt: string;
createdAt: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "rels".
*/
export interface Rel {
id: string;
updatedAt: string; updatedAt: string;
createdAt: string; createdAt: string;
} }
@@ -343,6 +369,24 @@ export interface Point {
updatedAt: string; updatedAt: string;
createdAt: string; createdAt: string;
} }
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "upload".
*/
export interface Upload {
id: string;
updatedAt: string;
createdAt: string;
url?: string | null;
thumbnailURL?: string | null;
filename?: string | null;
mimeType?: string | null;
filesize?: number | null;
width?: number | null;
height?: number | null;
focalX?: number | null;
focalY?: number | null;
}
/** /**
* This interface was referenced by `Config`'s JSON-Schema * This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "users". * via the `definition` "users".
@@ -391,6 +435,14 @@ export interface PayloadLockedDocument {
relationTo: 'points'; relationTo: 'points';
value: string | Point; value: string | Point;
} | null) } | null)
| ({
relationTo: 'upload';
value: string | Upload;
} | null)
| ({
relationTo: 'rels';
value: string | Rel;
} | null)
| ({ | ({
relationTo: 'users'; relationTo: 'users';
value: string | User; value: string | User;
@@ -487,6 +539,11 @@ export interface PostsSelect<T extends boolean = true> {
}; };
unnamedTabText?: T; unnamedTabText?: T;
unnamedTabNumber?: T; unnamedTabNumber?: T;
hasOne?: T;
hasMany?: T;
hasManyUpload?: T;
hasOnePoly?: T;
hasManyPoly?: T;
updatedAt?: T; updatedAt?: T;
createdAt?: T; createdAt?: T;
} }
@@ -705,6 +762,31 @@ export interface PointsSelect<T extends boolean = true> {
updatedAt?: T; updatedAt?: T;
createdAt?: T; createdAt?: T;
} }
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "upload_select".
*/
export interface UploadSelect<T extends boolean = true> {
updatedAt?: T;
createdAt?: T;
url?: T;
thumbnailURL?: T;
filename?: T;
mimeType?: T;
filesize?: T;
width?: T;
height?: T;
focalX?: T;
focalY?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "rels_select".
*/
export interface RelsSelect<T extends boolean = true> {
updatedAt?: T;
createdAt?: T;
}
/** /**
* This interface was referenced by `Config`'s JSON-Schema * This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "users_select". * via the `definition` "users_select".