From 5e3963482ebbfda886fa0097e6328ec0d3aaf599 Mon Sep 17 00:00:00 2001 From: Sasha <64744993+r1tsuu@users.noreply.github.com> Date: Thu, 12 Dec 2024 03:53:57 +0200 Subject: [PATCH] fix(db-postgres): `payload.db.upsert` inserts new rows instead of updating existing ones (#9916) ### What? `payload.db.updateOne` (and so `payload.db.upsert`) with drizzle adapters used incoming `where` incorrectly and worked properly only either if you passed `id` or some where query path required table joins (like `where: { 'array.title'`) which is also the reason why `upsert` _worked_ with user preferences specifically, because we need to join the `preferences_rels` table to query by `user.relationTo` and `user.value` Fixes https://github.com/payloadcms/payload/issues/9915 This was found here - https://github.com/payloadcms/payload/pull/9913, the database KV adapter uses `upsert` with `where` by unique fields. --- packages/drizzle/src/update.ts | 17 +++++++++++++++++ test/database/int.spec.ts | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/packages/drizzle/src/update.ts b/packages/drizzle/src/update.ts index 3c2c8dcf7b..86c41e0aac 100644 --- a/packages/drizzle/src/update.ts +++ b/packages/drizzle/src/update.ts @@ -4,6 +4,7 @@ import toSnakeCase from 'to-snake-case' import type { DrizzleAdapter } from './types.js' +import { buildFindManyArgs } from './find/buildFindManyArgs.js' import buildQuery from './queries/buildQuery.js' import { selectDistinct } from './queries/selectDistinct.js' import { upsertRow } from './upsertRow/index.js' @@ -38,6 +39,22 @@ export const updateOne: UpdateOne = async function updateOne( if (selectDistinctResult?.[0]?.id) { idToUpdate = selectDistinctResult?.[0]?.id + + // If id wasn't passed but `where` without any joins, retrieve it with findFirst + } else if (whereArg && !joins.length) { + const findManyArgs = buildFindManyArgs({ + adapter: this, + depth: 0, + fields: collection.flattenedFields, + joinQuery: false, + select: {}, + tableName, + }) + + findManyArgs.where = where + + const docToUpdate = await db.query[tableName].findFirst(findManyArgs) + idToUpdate = docToUpdate?.id } const result = await upsertRow({ diff --git a/test/database/int.spec.ts b/test/database/int.spec.ts index a2ad8d34d1..90a9569d70 100644 --- a/test/database/int.spec.ts +++ b/test/database/int.spec.ts @@ -1076,4 +1076,37 @@ describe('database', () => { expect(relationBDocs.docs).toHaveLength(0) }) + + it('should upsert', async () => { + const postShouldCreated = await payload.db.upsert({ + req: {}, + collection: 'posts', + data: { + title: 'some-title-here', + }, + where: { + title: { + equals: 'some-title-here', + }, + }, + }) + + expect(postShouldCreated).toBeTruthy() + + const postShouldUpdated = await payload.db.upsert({ + req: {}, + collection: 'posts', + data: { + title: 'some-title-here', + }, + where: { + title: { + equals: 'some-title-here', + }, + }, + }) + + // Should stay the same ID + expect(postShouldCreated.id).toBe(postShouldUpdated.id) + }) })