fix(db-postgres): querying multiple hasMany text or number fields (#14028)
Fixes https://github.com/payloadcms/payload/issues/14023
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import type { SQL, Table } from 'drizzle-orm'
|
import type { SQL } from 'drizzle-orm'
|
||||||
import type { SQLiteTableWithColumns } from 'drizzle-orm/sqlite-core'
|
import type { SQLiteTableWithColumns } from 'drizzle-orm/sqlite-core'
|
||||||
import type {
|
import type {
|
||||||
FlattenedBlock,
|
FlattenedBlock,
|
||||||
@@ -8,7 +8,7 @@ import type {
|
|||||||
TextField,
|
TextField,
|
||||||
} from 'payload'
|
} from 'payload'
|
||||||
|
|
||||||
import { and, eq, getTableName, like, sql } from 'drizzle-orm'
|
import { and, eq, getTableName, like, or, sql, Table } from 'drizzle-orm'
|
||||||
import { type PgTableWithColumns } from 'drizzle-orm/pg-core'
|
import { type PgTableWithColumns } from 'drizzle-orm/pg-core'
|
||||||
import { APIError, getFieldByPath } from 'payload'
|
import { APIError, getFieldByPath } from 'payload'
|
||||||
import { fieldShouldBeLocalized, tabHasName } from 'payload/shared'
|
import { fieldShouldBeLocalized, tabHasName } from 'payload/shared'
|
||||||
@@ -499,27 +499,37 @@ export const getTableColumnFromPath = ({
|
|||||||
columnName = 'number'
|
columnName = 'number'
|
||||||
}
|
}
|
||||||
newTableName = `${rootTableName}_${tableType}`
|
newTableName = `${rootTableName}_${tableType}`
|
||||||
|
|
||||||
|
const existingTable = joins.find((e) => e.queryPath === `${constraintPath}${field.name}`)
|
||||||
|
|
||||||
|
const table = (existingTable?.table ??
|
||||||
|
getTableAlias({ adapter, tableName: newTableName })
|
||||||
|
.newAliasTable) as PgTableWithColumns<any>
|
||||||
|
|
||||||
const joinConstraints = [
|
const joinConstraints = [
|
||||||
eq(adapter.tables[rootTableName].id, adapter.tables[newTableName].parent),
|
eq(adapter.tables[rootTableName].id, table.parent),
|
||||||
like(adapter.tables[newTableName].path, `${constraintPath}${field.name}`),
|
like(table.path, `${constraintPath}${field.name}`),
|
||||||
]
|
]
|
||||||
|
|
||||||
if (locale && isFieldLocalized && adapter.payload.config.localization) {
|
if (locale && isFieldLocalized && adapter.payload.config.localization) {
|
||||||
const conditions = [...joinConstraints]
|
const conditions = [...joinConstraints]
|
||||||
|
|
||||||
if (locale !== 'all') {
|
if (locale !== 'all') {
|
||||||
conditions.push(eq(adapter.tables[newTableName]._locale, locale))
|
conditions.push(eq(table._locale, locale))
|
||||||
}
|
}
|
||||||
|
|
||||||
addJoinTable({
|
addJoinTable({
|
||||||
condition: and(...conditions),
|
condition: and(...conditions),
|
||||||
joins,
|
joins,
|
||||||
table: adapter.tables[newTableName],
|
queryPath: `${constraintPath}${field.name}`,
|
||||||
|
table,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
addJoinTable({
|
addJoinTable({
|
||||||
condition: and(...joinConstraints),
|
condition: and(...joinConstraints),
|
||||||
joins,
|
joins,
|
||||||
table: adapter.tables[newTableName],
|
queryPath: `${constraintPath}${field.name}`,
|
||||||
|
table,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -527,7 +537,7 @@ export const getTableColumnFromPath = ({
|
|||||||
columnName,
|
columnName,
|
||||||
constraints,
|
constraints,
|
||||||
field,
|
field,
|
||||||
table: adapter.tables[newTableName],
|
table,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
|||||||
@@ -127,6 +127,11 @@ const TextFields: CollectionConfig = {
|
|||||||
type: 'text',
|
type: 'text',
|
||||||
hasMany: true,
|
hasMany: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'hasManySecond',
|
||||||
|
type: 'text',
|
||||||
|
hasMany: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'readOnlyHasMany',
|
name: 'readOnlyHasMany',
|
||||||
type: 'text',
|
type: 'text',
|
||||||
|
|||||||
@@ -165,6 +165,43 @@ describe('Fields', () => {
|
|||||||
expect(missResult).toBeFalsy()
|
expect(missResult).toBeFalsy()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should query multiple hasMany fields', async () => {
|
||||||
|
await payload.delete({ collection: 'text-fields', where: {} })
|
||||||
|
const hit = await payload.create({
|
||||||
|
collection: 'text-fields',
|
||||||
|
data: {
|
||||||
|
hasMany: ['1', '2', '3'],
|
||||||
|
hasManySecond: ['4'],
|
||||||
|
text: 'required',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const miss = await payload.create({
|
||||||
|
collection: 'text-fields',
|
||||||
|
data: {
|
||||||
|
hasMany: ['6'],
|
||||||
|
hasManySecond: ['4'],
|
||||||
|
text: 'required',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const { docs } = await payload.find({
|
||||||
|
collection: 'text-fields',
|
||||||
|
where: {
|
||||||
|
hasMany: { equals: '3' },
|
||||||
|
hasManySecond: {
|
||||||
|
equals: '4',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const hitResult = docs.find(({ id: findID }) => hit.id === findID)
|
||||||
|
const missResult = docs.find(({ id: findID }) => miss.id === findID)
|
||||||
|
|
||||||
|
expect(hitResult).toBeDefined()
|
||||||
|
expect(missResult).toBeFalsy()
|
||||||
|
})
|
||||||
|
|
||||||
it('should query like on value', async () => {
|
it('should query like on value', async () => {
|
||||||
const miss = await payload.create({
|
const miss = await payload.create({
|
||||||
collection: 'text-fields',
|
collection: 'text-fields',
|
||||||
|
|||||||
@@ -769,6 +769,7 @@ export interface TextField {
|
|||||||
fieldWithDefaultValue?: string | null;
|
fieldWithDefaultValue?: string | null;
|
||||||
dependentOnFieldWithDefaultValue?: string | null;
|
dependentOnFieldWithDefaultValue?: string | null;
|
||||||
hasMany?: string[] | null;
|
hasMany?: string[] | null;
|
||||||
|
hasManySecond?: string[] | null;
|
||||||
readOnlyHasMany?: string[] | null;
|
readOnlyHasMany?: string[] | null;
|
||||||
validatesHasMany?: string[] | null;
|
validatesHasMany?: string[] | null;
|
||||||
localizedHasMany?: string[] | null;
|
localizedHasMany?: string[] | null;
|
||||||
@@ -3256,6 +3257,7 @@ export interface TextFieldsSelect<T extends boolean = true> {
|
|||||||
fieldWithDefaultValue?: T;
|
fieldWithDefaultValue?: T;
|
||||||
dependentOnFieldWithDefaultValue?: T;
|
dependentOnFieldWithDefaultValue?: T;
|
||||||
hasMany?: T;
|
hasMany?: T;
|
||||||
|
hasManySecond?: T;
|
||||||
readOnlyHasMany?: T;
|
readOnlyHasMany?: T;
|
||||||
validatesHasMany?: T;
|
validatesHasMany?: T;
|
||||||
localizedHasMany?: T;
|
localizedHasMany?: T;
|
||||||
|
|||||||
Reference in New Issue
Block a user