perf: optimize virtual fields that reference ID (#12159)

This PR optimizes the new virtual fields with relationships feature
https://github.com/payloadcms/payload/pull/11805 when the path
references the ID field, for example:
```
{
  name: 'postCategoryID',
  type: 'number',
  virtual: 'post.category.id',
},
```

Previously, we did additional population of `category`, which is
unnecessary as we can always grab the ID from the `category` value
itself. One less querying step.
This commit is contained in:
Sasha
2025-04-18 21:39:55 +03:00
committed by GitHub
parent df7a3692f7
commit fdff5871f6
4 changed files with 113 additions and 12 deletions

View File

@@ -85,6 +85,11 @@ export const virtualFieldPopulationPromise = async ({
docID = currentValue docID = currentValue
} }
if (segments[0] === 'id' && segments.length === 0) {
siblingDoc[name] = docID
return
}
if (typeof docID !== 'string' && typeof docID !== 'number') { if (typeof docID !== 'string' && typeof docID !== 'number') {
return return
} }

View File

@@ -471,6 +471,16 @@ export default buildConfigWithDefaults({
type: 'text', type: 'text',
virtual: 'post.category.title', virtual: 'post.category.title',
}, },
{
name: 'postCategoryID',
type: 'json',
virtual: 'post.category.id',
},
{
name: 'postID',
type: 'json',
virtual: 'post.id',
},
{ {
name: 'postLocalized', name: 'postLocalized',
type: 'text', type: 'text',
@@ -481,6 +491,16 @@ export default buildConfigWithDefaults({
type: 'relationship', type: 'relationship',
relationTo: 'posts', relationTo: 'posts',
}, },
{
name: 'customID',
type: 'relationship',
relationTo: 'custom-ids',
},
{
name: 'customIDValue',
type: 'text',
virtual: 'customID.id',
},
], ],
versions: { drafts: true }, versions: { drafts: true },
}, },

View File

@@ -1993,11 +1993,63 @@ describe('database', () => {
collection: 'virtual-relations', collection: 'virtual-relations',
depth: 0, depth: 0,
where: { id: { equals: id } }, where: { id: { equals: id } },
draft: true,
}) })
expect(draft.docs[0]?.postTitle).toBe('my-title') expect(draft.docs[0]?.postTitle).toBe('my-title')
}) })
it('should allow virtual field as reference to ID', async () => {
const post = await payload.create({ collection: 'posts', data: { title: 'my-title' } })
const { id } = await payload.create({
collection: 'virtual-relations',
depth: 0,
data: { post: post.id },
})
const docDepth2 = await payload.findByID({ collection: 'virtual-relations', id })
expect(docDepth2.postID).toBe(post.id)
const docDepth0 = await payload.findByID({ collection: 'virtual-relations', id, depth: 0 })
expect(docDepth0.postID).toBe(post.id)
})
it('should allow virtual field as reference to custom ID', async () => {
const customID = await payload.create({ collection: 'custom-ids', data: {} })
const { id } = await payload.create({
collection: 'virtual-relations',
depth: 0,
data: { customID: customID.id },
})
const docDepth2 = await payload.findByID({ collection: 'virtual-relations', id })
expect(docDepth2.customIDValue).toBe(customID.id)
const docDepth0 = await payload.findByID({
collection: 'virtual-relations',
id,
depth: 0,
})
expect(docDepth0.customIDValue).toBe(customID.id)
})
it('should allow deep virtual field as reference to ID', async () => {
const category = await payload.create({
collection: 'categories',
data: { title: 'category-3' },
})
const post = await payload.create({
collection: 'posts',
data: { category: category.id, title: 'my-title-3' },
})
const { id } = await payload.create({
collection: 'virtual-relations',
depth: 0,
data: { post: post.id },
})
const docDepth2 = await payload.findByID({ collection: 'virtual-relations', id })
expect(docDepth2.postCategoryID).toBe(category.id)
const docDepth0 = await payload.findByID({ collection: 'virtual-relations', id, depth: 0 })
expect(docDepth0.postCategoryID).toBe(category.id)
})
it('should allow virtual field with reference localized', async () => { it('should allow virtual field with reference localized', async () => {
const post = await payload.create({ const post = await payload.create({
collection: 'posts', collection: 'posts',

View File

@@ -373,8 +373,39 @@ export interface VirtualRelation {
id: string; id: string;
postTitle?: string | null; postTitle?: string | null;
postCategoryTitle?: string | null; postCategoryTitle?: string | null;
postCategoryID?:
| {
[k: string]: unknown;
}
| unknown[]
| string
| number
| boolean
| null;
postID?:
| {
[k: string]: unknown;
}
| unknown[]
| string
| number
| boolean
| null;
postLocalized?: string | null; postLocalized?: string | null;
post?: (string | null) | Post; post?: (string | null) | Post;
customID?: (string | null) | CustomId;
customIDValue?: string | null;
updatedAt: string;
createdAt: string;
_status?: ('draft' | 'published') | null;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "custom-ids".
*/
export interface CustomId {
id: string;
title?: string | null;
updatedAt: string; updatedAt: string;
createdAt: string; createdAt: string;
_status?: ('draft' | 'published') | null; _status?: ('draft' | 'published') | null;
@@ -398,17 +429,6 @@ export interface FieldsPersistance {
updatedAt: string; updatedAt: string;
createdAt: string; createdAt: string;
} }
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "custom-ids".
*/
export interface CustomId {
id: string;
title?: string | null;
updatedAt: string;
createdAt: string;
_status?: ('draft' | 'published') | null;
}
/** /**
* This interface was referenced by `Config`'s JSON-Schema * This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "fake-custom-ids". * via the `definition` "fake-custom-ids".
@@ -807,8 +827,12 @@ export interface PlacesSelect<T extends boolean = true> {
export interface VirtualRelationsSelect<T extends boolean = true> { export interface VirtualRelationsSelect<T extends boolean = true> {
postTitle?: T; postTitle?: T;
postCategoryTitle?: T; postCategoryTitle?: T;
postCategoryID?: T;
postID?: T;
postLocalized?: T; postLocalized?: T;
post?: T; post?: T;
customID?: T;
customIDValue?: T;
updatedAt?: T; updatedAt?: T;
createdAt?: T; createdAt?: T;
_status?: T; _status?: T;