Type value lists

This commit is contained in:
Zoe Roux 2025-05-01 19:13:13 +02:00
parent 07a41bb175
commit 060c4d74b4
No known key found for this signature in database
4 changed files with 53 additions and 28 deletions

View File

@ -167,8 +167,14 @@ export const historyH = new Elysia({ tags: ["profiles"] })
async ({ body, jwt: { sub }, error }) => { async ({ body, jwt: { sub }, error }) => {
const profilePk = await getOrCreateProfile(sub); const profilePk = await getOrCreateProfile(sub);
const vals = values( const hist = values(
body.map((x) => ({ ...x, entryUseId: isUuid(x.entry) })), body.map((x) => ({ ...x, entryUseId: isUuid(x.entry) })),
{
percent: "integer",
time: "integer",
playedDate: "timestamptz",
videoId: "uuid",
},
).as("hist"); ).as("hist");
const valEqEntries = sql` const valEqEntries = sql`
case case
@ -185,13 +191,13 @@ export const historyH = new Elysia({ tags: ["profiles"] })
profilePk: sql`${profilePk}`, profilePk: sql`${profilePk}`,
entryPk: entries.pk, entryPk: entries.pk,
videoPk: videos.pk, videoPk: videos.pk,
percent: sql`hist.percent::integer`, percent: sql`hist.percent`,
time: sql`hist.time::integer`, time: sql`hist.time`,
playedDate: sql`hist.playedDate::timestamptz`, playedDate: sql`hist.playedDate`,
}) })
.from(vals) .from(hist)
.innerJoin(entries, valEqEntries) .innerJoin(entries, valEqEntries)
.leftJoin(videos, eq(videos.id, sql`hist.videoId::uuid`)), .leftJoin(videos, eq(videos.id, sql`hist.videoId`)),
) )
.returning({ pk: history.pk }); .returning({ pk: history.pk });
@ -249,7 +255,7 @@ export const historyH = new Elysia({ tags: ["profiles"] })
status: sql` status: sql`
case case
when when
hist.percent::integer >= 95 hist.percent >= 95
and ${nextEntryQ.pk} is null and ${nextEntryQ.pk} is null
then 'completed'::watchlist_status then 'completed'::watchlist_status
else 'watching'::watchlist_status else 'watching'::watchlist_status
@ -257,30 +263,30 @@ export const historyH = new Elysia({ tags: ["profiles"] })
`, `,
seenCount: sql` seenCount: sql`
case case
when ${entries.kind} = 'movie' then hist.percent::integer when ${entries.kind} = 'movie' then hist.percent
when hist.percent::integer >= 95 then 1 when hist.percent >= 95 then 1
else 0 else 0
end end
`, `,
nextEntry: sql` nextEntry: sql`
case case
when hist.percent::integer >= 95 then ${nextEntryQ.pk} when hist.percent >= 95 then ${nextEntryQ.pk}
else ${entries.pk} else ${entries.pk}
end end
`, `,
score: sql`null`, score: sql`null`,
startedAt: sql`hist.playedDate::timestamptz`, startedAt: sql`hist.playedDate`,
lastPlayedAt: sql`hist.playedDate::timestamptz`, lastPlayedAt: sql`hist.playedDate`,
completedAt: sql` completedAt: sql`
case case
when ${nextEntryQ.pk} is null then hist.playedDate::timestamptz when ${nextEntryQ.pk} is null then hist.playedDate
else null else null
end end
`, `,
// see https://github.com/drizzle-team/drizzle-orm/issues/3608 // see https://github.com/drizzle-team/drizzle-orm/issues/3608
updatedAt: sql`now()`, updatedAt: sql`now()`,
}) })
.from(vals) .from(hist)
.leftJoin(entries, valEqEntries) .leftJoin(entries, valEqEntries)
.leftJoinLateral(nextEntryQ, sql`true`), .leftJoinLateral(nextEntryQ, sql`true`),
) )

View File

@ -167,15 +167,21 @@ export const insertEntries = async (
.select( .select(
db db
.select({ .select({
entryPk: sql<number>`vids.entryPk::integer`.as("entry"), entryPk: sql<number>`vids.entryPk`.as("entry"),
videoPk: videos.pk, videoPk: videos.pk,
slug: computeVideoSlug( slug: computeVideoSlug(
sql`vids.entrySlug::text`, sql`vids.entrySlug`,
sql`vids.needRendering::boolean`, sql`vids.needRendering`,
), ),
}) })
.from(values(vids).as("vids")) .from(
.innerJoin(videos, eq(videos.id, sql`vids.videoId::uuid`)), values(vids, {
entryPk: "integer",
needRendering: "boolean",
videoId: "uuid",
}).as("vids"),
)
.innerJoin(videos, eq(videos.id, sql`vids.videoId`)),
) )
.onConflictDoNothing() .onConflictDoNothing()
.returning({ .returning({

View File

@ -230,14 +230,20 @@ export const videosH = new Elysia({ prefix: "/videos", tags: ["videos"] })
.select( .select(
db db
.select({ .select({
entry: entries.pk, entryPk: entriesQ.pk,
video: sql`j.video`, videoPk: sql`j.video`,
slug: computeVideoSlug( slug: computeVideoSlug(
entriesQ.showSlug, 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( .innerJoin(
entriesQ, entriesQ,
or( or(

View File

@ -74,15 +74,22 @@ export function sqlarr(array: unknown[]) {
} }
// See https://github.com/drizzle-team/drizzle-orm/issues/4044 // See https://github.com/drizzle-team/drizzle-orm/issues/4044
// TODO: type values (everything is a `text` for now) export function values<K extends string>(
export function values(items: Record<string, unknown>[]) { items: Record<K, unknown>[],
if (items[0] === undefined) throw new Error("Invalid values, expecting at least one items") typeInfo: Partial<Record<K, string>> = {},
const [firstProp, ...props] = Object.keys(items[0]); ) {
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 const values = items
.map((x) => { .map((x, i) => {
let ret = sql`(${x[firstProp]}`; let ret = sql`(${x[firstProp]}`;
if (i === 0 && typeInfo[firstProp])
ret = sql`${ret}::${sql.raw(typeInfo[firstProp])}`;
for (const val of props) { for (const val of props) {
ret = sql`${ret}, ${x[val]}`; ret = sql`${ret}, ${x[val]}`;
if (i === 0 && typeInfo[val])
ret = sql`${ret}::${sql.raw(typeInfo[val])}`;
} }
return sql`${ret})`; return sql`${ret})`;
}) })