fix(db-postgres): deleteOne handle joins (#5457)

* fix(db-postgres): deleteOne handle joins

* chore(db-postgres): reduce duplicate lines of code

* chore: optimize delete preferences

* chore(db-postgres): fix deleteOne regression

* chore(db-postgres): missing await
This commit is contained in:
Dan Ribbens
2024-03-26 13:36:15 -04:00
committed by GitHub
parent 20b4585666
commit 58e4174edb
6 changed files with 136 additions and 91 deletions

View File

@@ -1,47 +1,68 @@
import type { DeleteOne } from 'payload/database'
import type { PayloadRequest } from 'payload/types'
import { eq } from 'drizzle-orm'
import toSnakeCase from 'to-snake-case'
import type { PostgresAdapter } from './types.js'
import { buildFindManyArgs } from './find/buildFindManyArgs.js'
import buildQuery from './queries/buildQuery.js'
import { selectDistinct } from './queries/selectDistinct.js'
import { transform } from './transform/read/index.js'
export const deleteOne: DeleteOne = async function deleteOne(
this: PostgresAdapter,
{ collection, req = {} as PayloadRequest, where: incomingWhere },
{ collection: collectionSlug, req = {} as PayloadRequest, where: whereArg },
) {
const db = this.sessions[req.transactionID]?.db || this.drizzle
const collectionConfig = this.payload.collections[collection].config
const tableName = toSnakeCase(collection)
const collection = this.payload.collections[collectionSlug].config
const tableName = toSnakeCase(collectionSlug)
let docToDelete: Record<string, unknown>
const { where } = await buildQuery({
const { joinAliases, joins, selectFields, where } = await buildQuery({
adapter: this,
fields: collectionConfig.fields,
fields: collection.fields,
locale: req.locale,
tableName,
where: incomingWhere,
where: whereArg,
})
const findManyArgs = buildFindManyArgs({
const selectDistinctResult = await selectDistinct({
adapter: this,
depth: 0,
fields: collectionConfig.fields,
chainedMethods: [{ args: [1], method: 'limit' }],
db,
joinAliases,
joins,
selectFields,
tableName,
where,
})
findManyArgs.where = where
if (selectDistinctResult?.[0]?.id) {
docToDelete = await db.query[tableName].findFirst({
where: eq(this.tables[tableName].id, selectDistinctResult[0].id),
})
} else {
const findManyArgs = buildFindManyArgs({
adapter: this,
depth: 0,
fields: collection.fields,
tableName,
})
const docToDelete = await db.query[tableName].findFirst(findManyArgs)
findManyArgs.where = where
docToDelete = await db.query[tableName].findFirst(findManyArgs)
}
const result = transform({
config: this.payload.config,
data: docToDelete,
fields: collectionConfig.fields,
fields: collection.fields,
})
await db.delete(this.tables[tableName]).where(where)
await db.delete(this.tables[tableName]).where(eq(this.tables[tableName].id, docToDelete.id))
return result
}

View File

@@ -1,3 +1,5 @@
import type { QueryPromise } from 'drizzle-orm'
export type ChainedMethods = {
args: unknown[]
method: string
@@ -8,7 +10,7 @@ export type ChainedMethods = {
* @param methods
* @param query
*/
const chainMethods = ({ methods, query }): Promise<unknown> => {
const chainMethods = <T>({ methods, query }): QueryPromise<T> => {
return methods.reduce((query, { args, method }) => {
return query[method](...args)
}, query)

View File

@@ -7,6 +7,7 @@ import type { PostgresAdapter } from '../types.js'
import type { ChainedMethods } from './chainMethods.js'
import buildQuery from '../queries/buildQuery.js'
import { selectDistinct } from '../queries/selectDistinct.js'
import { transform } from '../transform/read/index.js'
import { buildFindManyArgs } from './buildFindManyArgs.js'
import { chainMethods } from './chainMethods.js'
@@ -39,7 +40,6 @@ export const findMany = async function find({
let hasPrevPage: boolean
let hasNextPage: boolean
let pagingCounter: number
let selectDistinctResult
const { joinAliases, joins, orderBy, selectFields, where } = await buildQuery({
adapter,
@@ -69,36 +69,21 @@ export const findMany = async function find({
tableName,
})
// only fetch IDs when a sort or where query is used that needs to be done on join tables, otherwise these can be done directly on the table in findMany
if (Object.keys(joins).length > 0 || joinAliases.length > 0) {
if (where) {
selectDistinctMethods.push({ args: [where], method: 'where' })
}
selectDistinctMethods.push({ args: [skip || (page - 1) * limit], method: 'offset' })
selectDistinctMethods.push({ args: [limit === 0 ? undefined : limit], method: 'limit' })
joinAliases.forEach(({ condition, table }) => {
selectDistinctMethods.push({
args: [table, condition],
method: 'leftJoin',
})
})
Object.entries(joins).forEach(([joinTable, condition]) => {
if (joinTable) {
selectDistinctMethods.push({
args: [adapter.tables[joinTable], condition],
method: 'leftJoin',
})
}
})
selectDistinctMethods.push({ args: [skip || (page - 1) * limit], method: 'offset' })
selectDistinctMethods.push({ args: [limit === 0 ? undefined : limit], method: 'limit' })
selectDistinctResult = await chainMethods({
methods: selectDistinctMethods,
query: db.selectDistinct(selectFields).from(table),
})
const selectDistinctResult = await selectDistinct({
adapter,
chainedMethods: selectDistinctMethods,
db,
joinAliases,
joins,
selectFields,
tableName,
where,
})
if (selectDistinctResult) {
if (selectDistinctResult.length === 0) {
return {
docs: [],
@@ -112,13 +97,14 @@ export const findMany = async function find({
totalDocs: 0,
totalPages: 0,
}
} else {
// set the id in an object for sorting later
selectDistinctResult.forEach(({ id }, i) => {
orderedIDMap[id] = i
})
orderedIDs = Object.keys(orderedIDMap)
findManyArgs.where = inArray(adapter.tables[tableName].id, orderedIDs)
}
// set the id in an object for sorting later
selectDistinctResult.forEach(({ id }, i) => {
orderedIDMap[id as number | string] = i
})
orderedIDs = Object.keys(orderedIDMap)
findManyArgs.where = inArray(adapter.tables[tableName].id, orderedIDs)
} else {
findManyArgs.limit = limitArg === 0 ? undefined : limitArg

View File

@@ -0,0 +1,60 @@
import type { QueryPromise, SQL } from 'drizzle-orm'
import type { ChainedMethods } from '../find/chainMethods.js'
import type { DrizzleDB, PostgresAdapter } from '../types.js'
import type { BuildQueryJoinAliases, BuildQueryJoins } from './buildQuery.js'
import { chainMethods } from '../find/chainMethods.js'
import { type GenericColumn } from '../types.js'
type Args = {
adapter: PostgresAdapter
chainedMethods?: ChainedMethods
db: DrizzleDB
joinAliases: BuildQueryJoinAliases
joins: BuildQueryJoins
selectFields: Record<string, GenericColumn>
tableName: string
where: SQL
}
/**
* Selects distinct records from a table only if there are joins that need to be used, otherwise return null
*/
export const selectDistinct = ({
adapter,
chainedMethods = [],
db,
joinAliases,
joins,
selectFields,
tableName,
where,
}: Args): QueryPromise<Record<string, GenericColumn> & { id: number | string }[]> => {
if (Object.keys(joins).length > 0 || joinAliases.length > 0) {
if (where) {
chainedMethods.push({ args: [where], method: 'where' })
}
joinAliases.forEach(({ condition, table }) => {
chainedMethods.push({
args: [table, condition],
method: 'leftJoin',
})
})
Object.entries(joins).forEach(([joinTable, condition]) => {
if (joinTable) {
chainedMethods.push({
args: [adapter.tables[joinTable], condition],
method: 'leftJoin',
})
}
})
return chainMethods({
methods: chainedMethods,
query: db.selectDistinct(selectFields).from(adapter.tables[tableName]),
})
}
}

View File

@@ -2,11 +2,10 @@ import type { UpdateOne } from 'payload/database'
import toSnakeCase from 'to-snake-case'
import type { ChainedMethods } from './find/chainMethods.js'
import type { PostgresAdapter } from './types.js'
import { chainMethods } from './find/chainMethods.js'
import buildQuery from './queries/buildQuery.js'
import { selectDistinct } from './queries/selectDistinct.js'
import { upsertRow } from './upsertRow/index.js'
export const updateOne: UpdateOne = async function updateOne(
@@ -17,6 +16,7 @@ export const updateOne: UpdateOne = async function updateOne(
const collection = this.payload.collections[collectionSlug].config
const tableName = toSnakeCase(collectionSlug)
const whereToUse = whereArg || { id: { equals: id } }
let idToUpdate = id
const { joinAliases, joins, selectFields, where } = await buildQuery({
adapter: this,
@@ -26,42 +26,19 @@ export const updateOne: UpdateOne = async function updateOne(
where: whereToUse,
})
let idToUpdate = id
const selectDistinctResult = await selectDistinct({
adapter: this,
chainedMethods: [{ args: [1], method: 'limit' }],
db,
joinAliases,
joins,
selectFields,
tableName,
where,
})
// only fetch IDs when a sort or where query is used that needs to be done on join tables, otherwise these can be done directly on the table in findMany
if (Object.keys(joins).length > 0 || joinAliases.length > 0) {
const selectDistinctMethods: ChainedMethods = []
if (where) {
selectDistinctMethods.push({ args: [where], method: 'where' })
}
joinAliases.forEach(({ condition, table }) => {
selectDistinctMethods.push({
args: [table, condition],
method: 'leftJoin',
})
})
Object.entries(joins).forEach(([joinTable, condition]) => {
if (joinTable) {
selectDistinctMethods.push({
args: [this.tables[joinTable], condition],
method: 'leftJoin',
})
}
})
selectDistinctMethods.push({ args: [1], method: 'limit' })
const selectDistinctResult = await chainMethods({
methods: selectDistinctMethods,
query: db.selectDistinct(selectFields).from(this.tables[tableName]),
})
if (selectDistinctResult?.[0]?.id) {
idToUpdate = selectDistinctResult?.[0]?.id
}
if (selectDistinctResult?.[0]?.id) {
idToUpdate = selectDistinctResult?.[0]?.id
}
const result = await upsertRow({

View File

@@ -31,15 +31,14 @@ async function deleteOperation(args: PreferenceRequest): Promise<Document> {
],
}
const result = await payload.delete({
const result = await payload.db.deleteOne({
collection: 'payload-preferences',
depth: 0,
user,
req,
where,
})
if (result.docs.length === 1) {
return result.docs[0]
if (result) {
return result
}
throw new NotFound(req.t)
}