diff --git a/api/src/controllers/profiles/history.ts b/api/src/controllers/profiles/history.ts index ec605ea1..660922c1 100644 --- a/api/src/controllers/profiles/history.ts +++ b/api/src/controllers/profiles/history.ts @@ -167,8 +167,14 @@ export const historyH = new Elysia({ tags: ["profiles"] }) async ({ body, jwt: { sub }, error }) => { const profilePk = await getOrCreateProfile(sub); - const vals = values( + const hist = values( body.map((x) => ({ ...x, entryUseId: isUuid(x.entry) })), + { + percent: "integer", + time: "integer", + playedDate: "timestamptz", + videoId: "uuid", + }, ).as("hist"); const valEqEntries = sql` case @@ -185,13 +191,13 @@ export const historyH = new Elysia({ tags: ["profiles"] }) profilePk: sql`${profilePk}`, entryPk: entries.pk, videoPk: videos.pk, - percent: sql`hist.percent::integer`, - time: sql`hist.time::integer`, - playedDate: sql`hist.playedDate::timestamptz`, + percent: sql`hist.percent`, + time: sql`hist.time`, + playedDate: sql`hist.playedDate`, }) - .from(vals) + .from(hist) .innerJoin(entries, valEqEntries) - .leftJoin(videos, eq(videos.id, sql`hist.videoId::uuid`)), + .leftJoin(videos, eq(videos.id, sql`hist.videoId`)), ) .returning({ pk: history.pk }); @@ -249,7 +255,7 @@ export const historyH = new Elysia({ tags: ["profiles"] }) status: sql` case when - hist.percent::integer >= 95 + hist.percent >= 95 and ${nextEntryQ.pk} is null then 'completed'::watchlist_status else 'watching'::watchlist_status @@ -257,30 +263,30 @@ export const historyH = new Elysia({ tags: ["profiles"] }) `, seenCount: sql` case - when ${entries.kind} = 'movie' then hist.percent::integer - when hist.percent::integer >= 95 then 1 + when ${entries.kind} = 'movie' then hist.percent + when hist.percent >= 95 then 1 else 0 end `, nextEntry: sql` case - when hist.percent::integer >= 95 then ${nextEntryQ.pk} + when hist.percent >= 95 then ${nextEntryQ.pk} else ${entries.pk} end `, score: sql`null`, - startedAt: sql`hist.playedDate::timestamptz`, - lastPlayedAt: sql`hist.playedDate::timestamptz`, + startedAt: sql`hist.playedDate`, + lastPlayedAt: sql`hist.playedDate`, completedAt: sql` case - when ${nextEntryQ.pk} is null then hist.playedDate::timestamptz + when ${nextEntryQ.pk} is null then hist.playedDate else null end `, // see https://github.com/drizzle-team/drizzle-orm/issues/3608 updatedAt: sql`now()`, }) - .from(vals) + .from(hist) .leftJoin(entries, valEqEntries) .leftJoinLateral(nextEntryQ, sql`true`), ) diff --git a/api/src/controllers/seed/insert/entries.ts b/api/src/controllers/seed/insert/entries.ts index 10ce07fa..ee62d4a8 100644 --- a/api/src/controllers/seed/insert/entries.ts +++ b/api/src/controllers/seed/insert/entries.ts @@ -167,15 +167,21 @@ export const insertEntries = async ( .select( db .select({ - entryPk: sql`vids.entryPk::integer`.as("entry"), + entryPk: sql`vids.entryPk`.as("entry"), videoPk: videos.pk, slug: computeVideoSlug( - sql`vids.entrySlug::text`, - sql`vids.needRendering::boolean`, + sql`vids.entrySlug`, + sql`vids.needRendering`, ), }) - .from(values(vids).as("vids")) - .innerJoin(videos, eq(videos.id, sql`vids.videoId::uuid`)), + .from( + values(vids, { + entryPk: "integer", + needRendering: "boolean", + videoId: "uuid", + }).as("vids"), + ) + .innerJoin(videos, eq(videos.id, sql`vids.videoId`)), ) .onConflictDoNothing() .returning({ diff --git a/api/src/controllers/videos.ts b/api/src/controllers/videos.ts index 3a8406d7..ded21415 100644 --- a/api/src/controllers/videos.ts +++ b/api/src/controllers/videos.ts @@ -230,14 +230,20 @@ export const videosH = new Elysia({ prefix: "/videos", tags: ["videos"] }) .select( db .select({ - entry: entries.pk, - video: sql`j.video`, + entryPk: entriesQ.pk, + videoPk: sql`j.video`, slug: computeVideoSlug( entriesQ.showSlug, - sql`j.needRendering::boolean || exists(${hasRenderingQ})`, + sql`j.needRendering || exists(${hasRenderingQ})`, ), }) - .from(values(vidEntries).as("j")) + .from( + values(vidEntries, { + video: "integer", + needRendering: "boolean", + entry: "jsonb", + }).as("j"), + ) .innerJoin( entriesQ, or( diff --git a/api/src/db/utils.ts b/api/src/db/utils.ts index 71950631..30bc0b83 100644 --- a/api/src/db/utils.ts +++ b/api/src/db/utils.ts @@ -74,15 +74,22 @@ export function sqlarr(array: unknown[]) { } // See https://github.com/drizzle-team/drizzle-orm/issues/4044 -// TODO: type values (everything is a `text` for now) -export function values(items: Record[]) { - if (items[0] === undefined) throw new Error("Invalid values, expecting at least one items") - const [firstProp, ...props] = Object.keys(items[0]); +export function values( + items: Record[], + typeInfo: Partial> = {}, +) { + if (items[0] === undefined) + throw new Error("Invalid values, expecting at least one items"); + const [firstProp, ...props] = Object.keys(items[0]) as K[]; const values = items - .map((x) => { + .map((x, i) => { let ret = sql`(${x[firstProp]}`; + if (i === 0 && typeInfo[firstProp]) + ret = sql`${ret}::${sql.raw(typeInfo[firstProp])}`; for (const val of props) { ret = sql`${ret}, ${x[val]}`; + if (i === 0 && typeInfo[val]) + ret = sql`${ret}::${sql.raw(typeInfo[val])}`; } return sql`${ret})`; })