wip: push movies could update items

This commit is contained in:
Zoe Roux 2024-11-30 17:30:12 +01:00
parent 55b3f1cc8c
commit cfe2cabfa4
No known key found for this signature in database
2 changed files with 45 additions and 12 deletions

View File

@ -10,9 +10,10 @@ import {
import type { SeedMovie } from "~/models/movie"; import type { SeedMovie } from "~/models/movie";
import { processOptImage } from "./images"; import { processOptImage } from "./images";
import { guessNextRefresh } from "./refresh"; import { guessNextRefresh } from "./refresh";
import { inArray, sql } from "drizzle-orm"; import { eq, getTableColumns, inArray, sql } from "drizzle-orm";
import { t } from "elysia"; import { t } from "elysia";
import { Resource } from "~/models/utils"; import { Resource } from "~/models/utils";
import { conflictUpdateAllExcept } from "~/db/schema/utils";
type Show = typeof shows.$inferInsert; type Show = typeof shows.$inferInsert;
type ShowTrans = typeof showTranslations.$inferInsert; type ShowTrans = typeof showTranslations.$inferInsert;
@ -41,12 +42,9 @@ export const seedMovie = async (
.values(movie) .values(movie)
.onConflictDoUpdate({ .onConflictDoUpdate({
target: shows.slug, target: shows.slug,
// we actually don't want to update anything, but we want to return the existing row. set: conflictUpdateAllExcept(shows, ["pk", "id", "slug", "createdAt"]),
// using a conflict update with a where false locks the database and ensure we don't have race conditions. // if year is different, this is not an update but a conflict (ex: dune-1984 vs dune-2021)
// it WONT work if we use triggers or need to handle conflicts on multiples collumns setWhere: sql`date_part('year', ${shows.startAir}) = date_part('year', excluded."start_air")`,
// see https://stackoverflow.com/questions/34708509/how-to-use-returning-with-on-conflict-in-postgresql for more
set: { id: sql`excluded.id` },
setWhere: sql`false`,
}) })
.returning({ .returning({
pk: shows.pk, pk: shows.pk,
@ -54,11 +52,18 @@ export const seedMovie = async (
slug: shows.slug, slug: shows.slug,
startAir: shows.startAir, startAir: shows.startAir,
// https://stackoverflow.com/questions/39058213/differentiate-inserted-and-updated-rows-in-upsert-using-system-columns/39204667#39204667 // https://stackoverflow.com/questions/39058213/differentiate-inserted-and-updated-rows-in-upsert-using-system-columns/39204667#39204667
conflict: sql`xmax = 0`.as("conflict"), updated: sql`(xmax = 0)`.as("updated"),
xmin: sql`xmin`,
xmax: sql`xmax`,
created: shows.createdAt,
}); });
if (ret.conflict) { // TODO: the `updated` bool is always false :c
console.log(`slug: ${ret.slug}, updated: ${ret.updated}`);
console.log(ret)
if (ret.updated) {
console.log("Updated!");
if (getYear(ret.startAir) === getYear(movie.startAir)) { if (getYear(ret.startAir) === getYear(movie.startAir)) {
return return;
} }
} }

View File

@ -5,6 +5,9 @@ import {
Table, Table,
View, View,
ViewBaseConfig, ViewBaseConfig,
getTableColumns,
sql,
SQL,
} from "drizzle-orm"; } from "drizzle-orm";
import type { AnyMySqlSelect } from "drizzle-orm/mysql-core"; import type { AnyMySqlSelect } from "drizzle-orm/mysql-core";
import { import {
@ -15,6 +18,8 @@ import {
} from "drizzle-orm/pg-core"; } from "drizzle-orm/pg-core";
import type { AnySQLiteSelect } from "drizzle-orm/sqlite-core"; import type { AnySQLiteSelect } from "drizzle-orm/sqlite-core";
import type { WithSubquery } from "drizzle-orm/subquery"; import type { WithSubquery } from "drizzle-orm/subquery";
import { db } from "..";
import { CasingCache } from "drizzle-orm/casing";
export const schema = pgSchema("kyoo"); export const schema = pgSchema("kyoo");
@ -24,11 +29,13 @@ export const image = () =>
jsonb().$type<{ id: string; source: string; blurhash: string }>(); jsonb().$type<{ id: string; source: string; blurhash: string }>();
// https://github.com/sindresorhus/type-fest/blob/main/source/simplify.d.ts#L58 // https://github.com/sindresorhus/type-fest/blob/main/source/simplify.d.ts#L58
type Simplify<T> = {[KeyType in keyof T]: T[KeyType]} & {}; type Simplify<T> = { [KeyType in keyof T]: T[KeyType] } & {};
// See https://github.com/drizzle-team/drizzle-orm/pull/1789 // See https://github.com/drizzle-team/drizzle-orm/pull/1789
type Select = AnyPgSelect | AnyMySqlSelect | AnySQLiteSelect; type Select = AnyPgSelect | AnyMySqlSelect | AnySQLiteSelect;
type AnySelect = Simplify<Omit<Select, "where"> & Partial<Pick<Select, "where">>>; type AnySelect = Simplify<
Omit<Select, "where"> & Partial<Pick<Select, "where">>
>;
export function getColumns< export function getColumns<
T extends T extends
| Table | Table
@ -49,3 +56,24 @@ export function getColumns<
? (table as any)[ViewBaseConfig].selectedFields ? (table as any)[ViewBaseConfig].selectedFields
: table._.selectedFields; : table._.selectedFields;
} }
// See https://github.com/drizzle-team/drizzle-orm/issues/1728
export function conflictUpdateAllExcept<
T extends Table,
E extends (keyof T["_"]["columns"])[],
>(table: T, except: E) {
const columns = getTableColumns(table);
const updateColumns = Object.entries(columns).filter(
([col]) => !except.includes(col),
);
return updateColumns.reduce(
(acc, [colName, col]) => {
// @ts-ignore: drizzle internal
const name = (db.dialect.casing as CasingCache).getColumnCasing(col);
acc[colName as keyof typeof acc] = sql.raw(`excluded."${name}"`);
return acc;
},
{} as Omit<Record<keyof T["_"]["columns"], SQL>, E[number]>,
);
}