feat: add find to payloadDataLoader to cache local API queries (#11685)

### What?
Extends our dataloader to add a momiozed payload find function. This way
it will cache the query for the same find request using a cacheKey from
find operation args.

### Why?
This was needed internally for `filterOptions` that exist in an array or
other sitautions where you have the same exact query being made and
awaited many times.

### How?

- Added `find` to payloadDataLoader. Marked `@experimental` in case it
needs to change.
- Created a cache key from the args
- Validate filterOptions changed from `payload.find` to
`payloadDataLoader.find`
- Made `payloadDataLoader` no longer optional on `PayloadRequest`, since
other args are required which are created from createLocalReq (context
for example), I don't see a reason why dataLoader shouldn't be required
also.

Example usage: 
```ts
const result = await req.payloadDataLoader.find({
    collection,
    req,
    where,
  })
```
This commit is contained in:
Dan Ribbens
2025-03-18 17:14:33 -04:00
committed by GitHub
parent 67a7358de1
commit 975bbb756f
8 changed files with 108 additions and 10 deletions

View File

@@ -1,5 +1,6 @@
/* eslint-disable jest/require-top-level-describe */
import { PostgresAdapter } from '@payloadcms/db-postgres/types'
import type { PostgresAdapter } from '@payloadcms/db-postgres/types'
import { cosineDistance, desc, gt, sql } from 'drizzle-orm'
import path from 'path'
import { buildConfig, getPayload } from 'payload'

View File

@@ -1,6 +1,7 @@
import type { Payload } from 'payload'
import type { CollectionSlug, Payload } from 'payload'
import path from 'path'
import { createLocalReq } from 'payload'
import { fileURLToPath } from 'url'
import type { NextRESTClient } from '../helpers/NextRESTClient.js'
@@ -28,7 +29,9 @@ describe('dataloader', () => {
},
})
if (loginResult.token) token = loginResult.token
if (loginResult.token) {
token = loginResult.token
}
})
afterAll(async () => {
@@ -187,4 +190,26 @@ describe('dataloader', () => {
expect(innerMostRelationship).toStrictEqual(relationB.id)
})
})
describe('find', () => {
it('should call the same query only once in a request', async () => {
const req = await createLocalReq({}, payload)
const spy = jest.spyOn(payload, 'find')
const findArgs = {
collection: 'items' as CollectionSlug,
req,
depth: 0,
where: {
name: { exists: true },
},
}
void req.payloadDataLoader.find(findArgs)
void req.payloadDataLoader.find(findArgs)
await req.payloadDataLoader.find(findArgs)
expect(spy).toHaveBeenCalledTimes(1)
})
})
})