Repository navigation
Set depth for a single relationship field in the Payload Local API find method to optimize peformance
#15244
|
I have a collection with many relationship fields, let's name it 'ProductSelection', with relationship fields like this:
And I need to create a page that should display all collections using the I plan to load all collections using the Payload Local API So, I need to specify only a concrete relationship field, which I need to populate, something like this: payload.find({
collection: 'product-selections',
depth: 0,
select: {
title: true,
owner: {
name: true,
},
},
});But this doesn't work. So, is there any possibility of achieving this using a single |
Replies: 3 comments 3 replies
|
payload doesnt support per-field depth, but you can achieve this with joins or custom query:
const result = await payload.find({
collection: "product-selections",
depth: 0,
joins: {
owner: true, // only populate owner
},
});
const result = await payload.find({
collection: "product-selections",
depth: 1,
select: {
title: true,
owner: true,
},
});with depth 1 only direct relationships get populated, nested ones stay as IDs
query {
ProductSelections {
docs {
title
owner {
name
}
}
}
}graphql naturally handles selective population
const [selections, users] = await Promise.all([
payload.find({ collection: "product-selections", depth: 0 }),
payload.find({ collection: "users", where: { id: { in: ownerIds } } }),
]);joins feature in v3 is probably what you need |
|
ah i see, yeah joins need the field on other side so wont work for your case about graphql locally - its not that bad actually. payload graphql resolver uses dataloader pattern internally so it batches the queries, not pure N+1. but yes there is some overhead compared to direct local api for your case i think best options:
const result = await payload.find({
collection: "product-selections",
depth: 1,
select: {
title: true,
owner: true, // will populate with depth 1
// dont include tags, subscribers, products - they stay as IDs
},
});when you dont select a relationship field, it shouldnt populate it even with depth 1. can you try this?
const selections = await payload.find({
collection: "product-selections",
depth: 0,
});
// get unique owner ids
const ownerIds = [...new Set(selections.docs.map(s => s.owner).filter(Boolean))];
// single query for all owners
const owners = await payload.find({
collection: "users",
where: { id: { in: ownerIds } },
limit: ownerIds.length,
});
// map them
const ownerMap = Object.fromEntries(owners.docs.map(u => [u.id, u]));
const result = selections.docs.map(s => ({
...s,
owner: ownerMap[s.owner],
}));only 2 queries total, doesnt matter how many selections you have
import { db } from "@payloadcms/db-postgres";
const result = await db
.select({
title: productSelections.title,
ownerName: users.name,
})
.from(productSelections)
.leftJoin(users, eq(productSelections.owner, users.id));this is most performant, single query with join honestly option 2 is cleanest for your case imo |
|
The provided workarounds work, but still be great to provide this feature from the Payload Local API out of the box, so I filed a separate feature request with this: #15249 |
ah i see, yeah joins need the field on other side so wont work for your case
about graphql locally - its not that bad actually. payload graphql resolver uses dataloader pattern internally so it batches the queries, not pure N+1. but yes there is some overhead compared to direct local api
for your case i think best options:
when you dont select a relationship field, it shouldnt populate it even with depth 1. can you…