fix(db-postgres): querying other collections via relationships inside blocks (#11255)
### What?
Previously, in postgres query like:
```ts
const result = await payload.find({
collection: 'blocks',
where: { 'blocks.director.name': { equals: 'Test Director' } },
})
```
where `blocks` is a blocks field, `director` is a relationship field and
`name` is a text field inside `directors`, failed with:

### Why?
The generated query before was a bit wrong.
Before:
```sql
select distinct
"blocks"."id",
"blocks"."created_at",
"blocks"."created_at"
from
"blocks"
left join "directors" "a5ad426a_eda4_4067_af7e_5b294d7f0968" on "a5ad426a_eda4_4067_af7e_5b294d7f0968"."id" = "blocks_blocks_some"."director_id"
left join "blocks_blocks_some" on "blocks"."id" = "blocks_blocks_some"."_parent_id"
where
"a5ad426a_eda4_4067_af7e_5b294d7f0968"."name" = 'Test Director'
order by
"blocks"."created_at" desc
limit
10
```
Notice `left join directors` _before_ join of `blocks_blocks_some`.
`blocks_blocks_some` doesn't exist yet, this PR changes so now we
generate
```sql
select distinct
"blocks"."id",
"blocks"."created_at",
"blocks"."created_at"
from
"blocks"
left join "blocks_blocks_some" on "blocks"."id" = "blocks_blocks_some"."_parent_id"
left join "directors" "a5ad426a_eda4_4067_af7e_5b294d7f0968" on "a5ad426a_eda4_4067_af7e_5b294d7f0968"."id" = "blocks_blocks_some"."director_id"
where
"a5ad426a_eda4_4067_af7e_5b294d7f0968"."name" = 'Test Director'
order by
"blocks"."created_at" desc
limit
10
```
This commit is contained in:
@@ -220,6 +220,37 @@ export const getTableColumnFromPath = ({
|
||||
let result: TableColumn
|
||||
const blockConstraints = []
|
||||
const blockSelectFields = {}
|
||||
|
||||
let blockJoin: BuildQueryJoinAliases[0]
|
||||
if (isFieldLocalized && adapter.payload.config.localization) {
|
||||
const conditions = [
|
||||
eq(
|
||||
(aliasTable || adapter.tables[tableName]).id,
|
||||
adapter.tables[newTableName]._parentID,
|
||||
),
|
||||
]
|
||||
|
||||
if (locale !== 'all') {
|
||||
conditions.push(eq(adapter.tables[newTableName]._locale, locale))
|
||||
}
|
||||
|
||||
blockJoin = {
|
||||
condition: and(...conditions),
|
||||
table: adapter.tables[newTableName],
|
||||
}
|
||||
} else {
|
||||
blockJoin = {
|
||||
condition: eq(
|
||||
(aliasTable || adapter.tables[tableName]).id,
|
||||
adapter.tables[newTableName]._parentID,
|
||||
),
|
||||
table: adapter.tables[newTableName],
|
||||
}
|
||||
}
|
||||
|
||||
// Create a new reference for nested joins
|
||||
const newJoins = [...joins]
|
||||
|
||||
try {
|
||||
result = getTableColumnFromPath({
|
||||
adapter,
|
||||
@@ -227,7 +258,7 @@ export const getTableColumnFromPath = ({
|
||||
constraintPath,
|
||||
constraints: blockConstraints,
|
||||
fields: block.flattenedFields,
|
||||
joins,
|
||||
joins: newJoins,
|
||||
locale,
|
||||
parentIsLocalized: parentIsLocalized || field.localized,
|
||||
pathSegments: pathSegments.slice(1),
|
||||
@@ -246,30 +277,14 @@ export const getTableColumnFromPath = ({
|
||||
blockTableColumn = result
|
||||
constraints = constraints.concat(blockConstraints)
|
||||
selectFields = { ...selectFields, ...blockSelectFields }
|
||||
if (isFieldLocalized && adapter.payload.config.localization) {
|
||||
const conditions = [
|
||||
eq(
|
||||
(aliasTable || adapter.tables[tableName]).id,
|
||||
adapter.tables[newTableName]._parentID,
|
||||
),
|
||||
]
|
||||
|
||||
if (locale !== 'all') {
|
||||
conditions.push(eq(adapter.tables[newTableName]._locale, locale))
|
||||
const previousLength = joins.length
|
||||
joins.push(blockJoin)
|
||||
// Append new joins AFTER the block join to prevent errors with missing FROM clause.
|
||||
if (newJoins.length > previousLength) {
|
||||
for (let i = previousLength; i < newJoins.length; i++) {
|
||||
joins.push(newJoins[i])
|
||||
}
|
||||
|
||||
joins.push({
|
||||
condition: and(...conditions),
|
||||
table: adapter.tables[newTableName],
|
||||
})
|
||||
} else {
|
||||
joins.push({
|
||||
condition: eq(
|
||||
(aliasTable || adapter.tables[tableName]).id,
|
||||
adapter.tables[newTableName]._parentID,
|
||||
),
|
||||
table: adapter.tables[newTableName],
|
||||
})
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
@@ -460,6 +460,27 @@ export default buildConfigWithDefaults({
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
slug: 'blocks',
|
||||
fields: [
|
||||
{
|
||||
type: 'blocks',
|
||||
name: 'blocks',
|
||||
blocks: [
|
||||
{
|
||||
slug: 'some',
|
||||
fields: [
|
||||
{
|
||||
type: 'relationship',
|
||||
relationTo: 'directors',
|
||||
name: 'director',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
onInit: async (payload) => {
|
||||
await payload.create({
|
||||
|
||||
@@ -1130,6 +1130,36 @@ describe('Relationships', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('should allow querying within block nesting', async () => {
|
||||
const director = await payload.create({
|
||||
collection: 'directors',
|
||||
data: { name: 'Test Director' },
|
||||
})
|
||||
|
||||
const director_false = await payload.create({
|
||||
collection: 'directors',
|
||||
data: { name: 'False Director' },
|
||||
})
|
||||
|
||||
const doc = await payload.create({
|
||||
collection: 'blocks',
|
||||
data: { blocks: [{ blockType: 'some', director: director.id }] },
|
||||
})
|
||||
|
||||
await payload.create({
|
||||
collection: 'blocks',
|
||||
data: { blocks: [{ blockType: 'some', director: director_false.id }] },
|
||||
})
|
||||
|
||||
const result = await payload.find({
|
||||
collection: 'blocks',
|
||||
where: { 'blocks.director.name': { equals: 'Test Director' } },
|
||||
})
|
||||
|
||||
expect(result.totalDocs).toBe(1)
|
||||
expect(result.docs[0]!.id).toBe(doc.id)
|
||||
})
|
||||
|
||||
describe('Nested Querying Separate Collections', () => {
|
||||
let director: Director
|
||||
|
||||
|
||||
@@ -86,6 +86,7 @@ export interface Config {
|
||||
'deep-nested': DeepNested;
|
||||
relations: Relation1;
|
||||
items: Item;
|
||||
blocks: Block;
|
||||
users: User;
|
||||
'payload-locked-documents': PayloadLockedDocument;
|
||||
'payload-preferences': PayloadPreference;
|
||||
@@ -117,6 +118,7 @@ export interface Config {
|
||||
'deep-nested': DeepNestedSelect<false> | DeepNestedSelect<true>;
|
||||
relations: RelationsSelect<false> | RelationsSelect<true>;
|
||||
items: ItemsSelect<false> | ItemsSelect<true>;
|
||||
blocks: BlocksSelect<false> | BlocksSelect<true>;
|
||||
users: UsersSelect<false> | UsersSelect<true>;
|
||||
'payload-locked-documents': PayloadLockedDocumentsSelect<false> | PayloadLockedDocumentsSelect<true>;
|
||||
'payload-preferences': PayloadPreferencesSelect<false> | PayloadPreferencesSelect<true>;
|
||||
@@ -464,6 +466,23 @@ export interface Item {
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "blocks".
|
||||
*/
|
||||
export interface Block {
|
||||
id: string;
|
||||
blocks?:
|
||||
| {
|
||||
director?: (string | null) | Director;
|
||||
id?: string | null;
|
||||
blockName?: string | null;
|
||||
blockType: 'some';
|
||||
}[]
|
||||
| null;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "payload-locked-documents".
|
||||
@@ -551,6 +570,10 @@ export interface PayloadLockedDocument {
|
||||
relationTo: 'items';
|
||||
value: string | Item;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'blocks';
|
||||
value: string | Block;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'users';
|
||||
value: string | User;
|
||||
@@ -840,6 +863,25 @@ export interface ItemsSelect<T extends boolean = true> {
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "blocks_select".
|
||||
*/
|
||||
export interface BlocksSelect<T extends boolean = true> {
|
||||
blocks?:
|
||||
| T
|
||||
| {
|
||||
some?:
|
||||
| T
|
||||
| {
|
||||
director?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
};
|
||||
};
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "users_select".
|
||||
|
||||
Reference in New Issue
Block a user