db: remove table suffix from schemas

This commit is contained in:
2026-02-05 19:28:30 -08:00
parent 88218b3567
commit 53f91facc9
18 changed files with 249 additions and 279 deletions

View File

@@ -1,11 +1,5 @@
import { desc, eq, like } from "drizzle-orm"; import { desc, eq, like } from "drizzle-orm";
import { import { anime, animeSongLinks, artists, groups, songs } from "$lib/db/schema";
animeSongLinksTable,
animeTable,
artistsTable,
groupsTable,
songsTable,
} from "$lib/db/schema";
import type { ClientDb } from "./index"; import type { ClientDb } from "./index";
/** /**
@@ -31,18 +25,14 @@ export async function getAnimeList(db: ClientDb, limit = DEFAULT_LIST_LIMIT) {
// Explicit selection keeps payload small and gives a stable, inferred return type // Explicit selection keeps payload small and gives a stable, inferred return type
return db return db
.select({ .select({
annId: animeTable.annId, annId: anime.annId,
mainName: animeTable.mainName, mainName: anime.mainName,
year: animeTable.year, year: anime.year,
seasonId: animeTable.seasonId, seasonId: anime.seasonId,
malId: animeTable.malId, malId: anime.malId,
}) })
.from(animeTable) .from(anime)
.orderBy( .orderBy(desc(anime.year), desc(anime.seasonId), desc(anime.annId))
desc(animeTable.year),
desc(animeTable.seasonId),
desc(animeTable.annId),
)
.limit(safeLimit); .limit(safeLimit);
} }
@@ -65,19 +55,15 @@ export async function searchAnimeByName(
return db return db
.select({ .select({
annId: animeTable.annId, annId: anime.annId,
mainName: animeTable.mainName, mainName: anime.mainName,
year: animeTable.year, year: anime.year,
seasonId: animeTable.seasonId, seasonId: anime.seasonId,
malId: animeTable.malId, malId: anime.malId,
}) })
.from(animeTable) .from(anime)
.where(like(animeTable.mainName, pattern)) .where(like(anime.mainName, pattern))
.orderBy( .orderBy(desc(anime.year), desc(anime.seasonId), desc(anime.annId))
desc(animeTable.year),
desc(animeTable.seasonId),
desc(animeTable.annId),
)
.limit(safeLimit); .limit(safeLimit);
} }
@@ -93,43 +79,37 @@ export async function searchAnimeByName(
export async function getAnimeWithSongsByAnnId(db: ClientDb, annId: number) { export async function getAnimeWithSongsByAnnId(db: ClientDb, annId: number) {
const animeRows = await db const animeRows = await db
.select({ .select({
annId: animeTable.annId, annId: anime.annId,
mainName: animeTable.mainName, mainName: anime.mainName,
year: animeTable.year, year: anime.year,
seasonId: animeTable.seasonId, seasonId: anime.seasonId,
malId: animeTable.malId, malId: anime.malId,
}) })
.from(animeTable) .from(anime)
.where(eq(animeTable.annId, annId)) .where(eq(anime.annId, annId))
.limit(1); .limit(1);
const anime = animeRows[0]; const foundAnime = animeRows[0];
if (!anime) return null; if (!foundAnime) return null;
const rows = await db const rows = await db
.select({ .select({
annSongId: animeSongLinksTable.annSongId, annSongId: animeSongLinks.annSongId,
type: animeSongLinksTable.type, type: animeSongLinks.type,
number: animeSongLinksTable.number, number: animeSongLinks.number,
songName: songsTable.name, songName: songs.name,
fileName: songsTable.fileName, fileName: songs.fileName,
artistName: artistsTable.name, artistName: artists.name,
groupName: groupsTable.name, groupName: groups.name,
}) })
.from(animeSongLinksTable) .from(animeSongLinks)
.innerJoin( .innerJoin(songs, eq(songs.annSongId, animeSongLinks.annSongId))
songsTable, .leftJoin(artists, eq(artists.songArtistId, songs.songArtistId))
eq(songsTable.annSongId, animeSongLinksTable.annSongId), .leftJoin(groups, eq(groups.songGroupId, songs.songGroupId))
) .where(eq(animeSongLinks.annId, annId))
.leftJoin( .orderBy(desc(animeSongLinks.type), desc(animeSongLinks.number));
artistsTable,
eq(artistsTable.songArtistId, songsTable.songArtistId),
)
.leftJoin(groupsTable, eq(groupsTable.songGroupId, songsTable.songGroupId))
.where(eq(animeSongLinksTable.annId, annId))
.orderBy(desc(animeSongLinksTable.type), desc(animeSongLinksTable.number));
return { return {
anime, anime,

View File

@@ -14,21 +14,21 @@ import amqGroupsJson from "../../../data/amq-groups.json" with { type: "json" };
import amqSongsJson from "../../../data/amq-songs.json" with { type: "json" }; import amqSongsJson from "../../../data/amq-songs.json" with { type: "json" };
import { db } from "./index"; import { db } from "./index";
import { import {
animeGenresTable, anime,
animeNamesTable, animeGenres,
animeSongLinksTable, animeNames,
animeTable, animeSongLinks,
animeTagsTable, animeTags,
artistAltNamesTable, artistAltNames,
artistGroupsTable, artistGroups,
artistsTable, artists,
genresTable, genres,
groupAltNamesTable, groupAltNames,
groupArtistMembersTable, groupArtistMembers,
groupGroupMembersTable, groupGroupMembers,
groupsTable, groups,
songsTable, songs,
tagsTable, tags,
} from "./schema"; } from "./schema";
/** /**
@@ -169,15 +169,15 @@ export async function importAmqData(
); );
} }
const anime = animeParsed.data; const animeData = animeParsed.data;
const songs = songsParsed.data; const songsData = songsParsed.data;
const artists = artistsParsed.data; const artistsData = artistsParsed.data;
const groups = groupsParsed.data; const groupsData = groupsParsed.data;
if (verbose) { if (verbose) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.info( console.info(
`AMQ import: ${anime.length} anime, ${songs.length} songs, ${artists.length} artists, ${groups.length} groups`, `AMQ import: ${animeData.length} anime, ${songsData.length} songs, ${artistsData.length} artists, ${groupsData.length} groups`,
); );
} }
@@ -185,45 +185,45 @@ export async function importAmqData(
db.transaction(() => { db.transaction(() => {
if (wipeFirst) { if (wipeFirst) {
// Child tables first, then parents (respect FKs) // Child tables first, then parents (respect FKs)
db.delete(animeSongLinksTable).run(); db.delete(animeSongLinks).run();
db.delete(animeNamesTable).run(); db.delete(animeNames).run();
db.delete(animeGenresTable).run(); db.delete(animeGenres).run();
db.delete(animeTagsTable).run(); db.delete(animeTags).run();
// artist/group graph tables // artist/group graph tables
db.delete(groupGroupMembersTable).run(); db.delete(groupGroupMembers).run();
db.delete(groupArtistMembersTable).run(); db.delete(groupArtistMembers).run();
db.delete(artistGroupsTable).run(); db.delete(artistGroups).run();
db.delete(groupAltNamesTable).run(); db.delete(groupAltNames).run();
db.delete(artistAltNamesTable).run(); db.delete(artistAltNames).run();
db.delete(animeTable).run(); db.delete(anime).run();
// songs referenced by anime_song_links; delete after links wiped // songs referenced by anime_song_links; delete after links wiped
db.delete(songsTable).run(); db.delete(songs).run();
// groups referenced by graph tables // groups referenced by graph tables
db.delete(groupsTable).run(); db.delete(groups).run();
// artists referenced by graph tables // artists referenced by graph tables
db.delete(artistsTable).run(); db.delete(artists).run();
} }
// 2a) Insert artists (core) // 2a) Insert artists (core)
{ {
const artistRows = artists.map((a) => ({ const artistRows = artistsData.map((a) => ({
songArtistId: a.songArtistId, songArtistId: a.songArtistId,
name: a.name, name: a.name,
})); }));
for (const batch of chunk(artistRows, batchSize)) { for (const batch of chunk(artistRows, batchSize)) {
if (batch.length === 0) continue; if (batch.length === 0) continue;
db.insert(artistsTable) db.insert(artists)
.values(batch) .values(batch)
.onConflictDoUpdate({ .onConflictDoUpdate({
target: artistsTable.songArtistId, target: artists.songArtistId,
set: { set: {
name: artistsTable.name, name: artists.name,
}, },
}) })
.run(); .run();
@@ -232,19 +232,19 @@ export async function importAmqData(
// 2a.1) Insert groups (core) // 2a.1) Insert groups (core)
{ {
const groupRows = groups.map((g) => ({ const groupRows = groupsData.map((g) => ({
songGroupId: g.songGroupId, songGroupId: g.songGroupId,
name: g.name, name: g.name,
})); }));
for (const batch of chunk(groupRows, batchSize)) { for (const batch of chunk(groupRows, batchSize)) {
if (batch.length === 0) continue; if (batch.length === 0) continue;
db.insert(groupsTable) db.insert(groups)
.values(batch) .values(batch)
.onConflictDoUpdate({ .onConflictDoUpdate({
target: groupsTable.songGroupId, target: groups.songGroupId,
set: { set: {
name: groupsTable.name, name: groups.name,
}, },
}) })
.run(); .run();
@@ -255,14 +255,14 @@ export async function importAmqData(
// For these join tables, easiest/most consistent is replace-all (delete then insert), // For these join tables, easiest/most consistent is replace-all (delete then insert),
// since theyre derived arrays and can change over time. // since theyre derived arrays and can change over time.
{ {
db.delete(artistGroupsTable).run(); db.delete(artistGroups).run();
db.delete(groupArtistMembersTable).run(); db.delete(groupArtistMembers).run();
db.delete(groupGroupMembersTable).run(); db.delete(groupGroupMembers).run();
db.delete(artistAltNamesTable).run(); db.delete(artistAltNames).run();
db.delete(groupAltNamesTable).run(); db.delete(groupAltNames).run();
// artist -> groups // artist -> groups
const artistGroupRows = artists.flatMap((a) => const artistGroupRows = artistsData.flatMap((a) =>
a.inGroups.map((songGroupId) => ({ a.inGroups.map((songGroupId) => ({
songArtistId: a.songArtistId, songArtistId: a.songArtistId,
songGroupId, songGroupId,
@@ -270,11 +270,11 @@ export async function importAmqData(
); );
for (const batch of chunk(artistGroupRows, batchSize)) { for (const batch of chunk(artistGroupRows, batchSize)) {
if (batch.length === 0) continue; if (batch.length === 0) continue;
db.insert(artistGroupsTable).values(batch).run(); db.insert(artistGroups).values(batch).run();
} }
// group -> artist members // group -> artist members
const groupArtistMemberRows = groups.flatMap((g) => const groupArtistMemberRows = groupsData.flatMap((g) =>
g.artistMembers.map((songArtistId) => ({ g.artistMembers.map((songArtistId) => ({
songGroupId: g.songGroupId, songGroupId: g.songGroupId,
songArtistId, songArtistId,
@@ -282,11 +282,11 @@ export async function importAmqData(
); );
for (const batch of chunk(groupArtistMemberRows, batchSize)) { for (const batch of chunk(groupArtistMemberRows, batchSize)) {
if (batch.length === 0) continue; if (batch.length === 0) continue;
db.insert(groupArtistMembersTable).values(batch).run(); db.insert(groupArtistMembers).values(batch).run();
} }
// group -> group members // group -> group members
const groupGroupMemberRows = groups.flatMap((g) => const groupGroupMemberRows = groupsData.flatMap((g) =>
g.groupMembers.map((memberSongGroupId) => ({ g.groupMembers.map((memberSongGroupId) => ({
songGroupId: g.songGroupId, songGroupId: g.songGroupId,
memberSongGroupId, memberSongGroupId,
@@ -294,11 +294,11 @@ export async function importAmqData(
); );
for (const batch of chunk(groupGroupMemberRows, batchSize)) { for (const batch of chunk(groupGroupMemberRows, batchSize)) {
if (batch.length === 0) continue; if (batch.length === 0) continue;
db.insert(groupGroupMembersTable).values(batch).run(); db.insert(groupGroupMembers).values(batch).run();
} }
// artist alt names: { songArtistId, name } (stored as altSongArtistId in DB) // artist alt names: { songArtistId, name } (stored as altSongArtistId in DB)
const artistAltNameRows = artists.flatMap((a) => const artistAltNameRows = artistsData.flatMap((a) =>
a.altNames.map((alt) => ({ a.altNames.map((alt) => ({
songArtistId: a.songArtistId, songArtistId: a.songArtistId,
altSongArtistId: alt.songArtistId, altSongArtistId: alt.songArtistId,
@@ -307,11 +307,11 @@ export async function importAmqData(
); );
for (const batch of chunk(artistAltNameRows, batchSize)) { for (const batch of chunk(artistAltNameRows, batchSize)) {
if (batch.length === 0) continue; if (batch.length === 0) continue;
db.insert(artistAltNamesTable).values(batch).run(); db.insert(artistAltNames).values(batch).run();
} }
// group alt names: { songGroupId, name } where object.songGroupId is the context // group alt names: { songGroupId, name } where object.songGroupId is the context
const groupAltNameRows = groups.flatMap((g) => const groupAltNameRows = groupsData.flatMap((g) =>
g.altNames.map((alt) => ({ g.altNames.map((alt) => ({
songGroupId: g.songGroupId, songGroupId: g.songGroupId,
contextSongGroupId: alt.songGroupId, contextSongGroupId: alt.songGroupId,
@@ -320,13 +320,13 @@ export async function importAmqData(
); );
for (const batch of chunk(groupAltNameRows, batchSize)) { for (const batch of chunk(groupAltNameRows, batchSize)) {
if (batch.length === 0) continue; if (batch.length === 0) continue;
db.insert(groupAltNamesTable).values(batch).run(); db.insert(groupAltNames).values(batch).run();
} }
} }
// 2b) Insert songs // 2b) Insert songs
{ {
const songRows = songs.map((s) => ({ const songRows = songsData.map((s) => ({
annSongId: s.annSongId, annSongId: s.annSongId,
songId: s.songId, songId: s.songId,
name: s.name, name: s.name,
@@ -345,21 +345,21 @@ export async function importAmqData(
for (const batch of chunk(songRows, batchSize)) { for (const batch of chunk(songRows, batchSize)) {
if (batch.length === 0) continue; if (batch.length === 0) continue;
db.insert(songsTable) db.insert(songs)
.values(batch) .values(batch)
.onConflictDoUpdate({ .onConflictDoUpdate({
target: songsTable.annSongId, target: songs.annSongId,
set: { set: {
songId: songsTable.songId, songId: songs.songId,
name: songsTable.name, name: songs.name,
category: songsTable.category, category: songs.category,
fileName: songsTable.fileName, fileName: songs.fileName,
songArtistId: songsTable.songArtistId, songArtistId: songs.songArtistId,
songGroupId: songsTable.songGroupId, songGroupId: songs.songGroupId,
composerArtistId: songsTable.composerArtistId, composerArtistId: songs.composerArtistId,
composerGroupId: songsTable.composerGroupId, composerGroupId: songs.composerGroupId,
arrangerArtistId: songsTable.arrangerArtistId, arrangerArtistId: songs.arrangerArtistId,
arrangerGroupId: songsTable.arrangerGroupId, arrangerGroupId: songs.arrangerGroupId,
}, },
}) })
.run(); .run();
@@ -368,7 +368,7 @@ export async function importAmqData(
// 2c) Insert anime and its normalized children // 2c) Insert anime and its normalized children
{ {
const animeRows = anime.map((a) => ({ const animeRows = animeData.map((a) => ({
annId: a.annId, annId: a.annId,
aniListId: a.aniListId, aniListId: a.aniListId,
malId: a.malId, malId: a.malId,
@@ -387,24 +387,24 @@ export async function importAmqData(
for (const batch of chunk(animeRows, batchSize)) { for (const batch of chunk(animeRows, batchSize)) {
if (batch.length === 0) continue; if (batch.length === 0) continue;
db.insert(animeTable) db.insert(anime)
.values(batch) .values(batch)
.onConflictDoUpdate({ .onConflictDoUpdate({
target: animeTable.annId, target: anime.annId,
set: { set: {
aniListId: animeTable.aniListId, aniListId: anime.aniListId,
malId: animeTable.malId, malId: anime.malId,
kitsuId: animeTable.kitsuId, kitsuId: anime.kitsuId,
categoryName: animeTable.categoryName, categoryName: anime.categoryName,
categoryNumber: animeTable.categoryNumber, categoryNumber: anime.categoryNumber,
mainName: animeTable.mainName, mainName: anime.mainName,
mainNameEn: animeTable.mainNameEn, mainNameEn: anime.mainNameEn,
mainNameJa: animeTable.mainNameJa, mainNameJa: anime.mainNameJa,
year: animeTable.year, year: anime.year,
seasonId: animeTable.seasonId, seasonId: anime.seasonId,
opCount: animeTable.opCount, opCount: anime.opCount,
edCount: animeTable.edCount, edCount: anime.edCount,
insertCount: animeTable.insertCount, insertCount: anime.insertCount,
}, },
}) })
.run(); .run();
@@ -412,23 +412,17 @@ export async function importAmqData(
// For child tables, simplest is: delete existing for these annIds then insert fresh. // For child tables, simplest is: delete existing for these annIds then insert fresh.
// (Safer than trying to upsert composite unique constraints for each child row.) // (Safer than trying to upsert composite unique constraints for each child row.)
for (const a of anime) { for (const a of animeData) {
db.delete(animeNamesTable) db.delete(animeNames).where(eq(animeNames.annId, a.annId)).run();
.where(eq(animeNamesTable.annId, a.annId)) db.delete(animeGenres).where(eq(animeGenres.annId, a.annId)).run();
.run(); db.delete(animeTags).where(eq(animeTags.annId, a.annId)).run();
db.delete(animeGenresTable) db.delete(animeSongLinks)
.where(eq(animeGenresTable.annId, a.annId)) .where(eq(animeSongLinks.annId, a.annId))
.run();
db.delete(animeTagsTable)
.where(eq(animeTagsTable.annId, a.annId))
.run();
db.delete(animeSongLinksTable)
.where(eq(animeSongLinksTable.annId, a.annId))
.run(); .run();
// names // names
if (a.names.length) { if (a.names.length) {
db.insert(animeNamesTable) db.insert(animeNames)
.values( .values(
a.names.map((n) => ({ a.names.map((n) => ({
annId: a.annId, annId: a.annId,
@@ -442,13 +436,13 @@ export async function importAmqData(
// genres // genres
if (a.genres.length) { if (a.genres.length) {
// Ensure lookup rows exist (string PK) // Ensure lookup rows exist (string PK)
db.insert(genresTable) db.insert(genres)
.values(a.genres.map((g) => ({ name: g }))) .values(a.genres.map((g) => ({ name: g })))
.onConflictDoNothing() .onConflictDoNothing()
.run(); .run();
// Insert relations // Insert relations
db.insert(animeGenresTable) db.insert(animeGenres)
.values( .values(
a.genres.map((g) => ({ a.genres.map((g) => ({
annId: a.annId, annId: a.annId,
@@ -461,13 +455,13 @@ export async function importAmqData(
// tags // tags
if (a.tags.length) { if (a.tags.length) {
// Ensure lookup rows exist (string PK) // Ensure lookup rows exist (string PK)
db.insert(tagsTable) db.insert(tags)
.values(a.tags.map((t) => ({ name: t }))) .values(a.tags.map((t) => ({ name: t })))
.onConflictDoNothing() .onConflictDoNothing()
.run(); .run();
// Insert relations // Insert relations
db.insert(animeTagsTable) db.insert(animeTags)
.values( .values(
a.tags.map((t) => ({ a.tags.map((t) => ({
annId: a.annId, annId: a.annId,
@@ -479,7 +473,7 @@ export async function importAmqData(
// song links // song links
if (a.songLinks.length) { if (a.songLinks.length) {
db.insert(animeSongLinksTable) db.insert(animeSongLinks)
.values( .values(
a.songLinks.map((l) => ({ a.songLinks.map((l) => ({
annId: a.annId, annId: a.annId,

View File

@@ -1,87 +1,84 @@
import { relations } from "drizzle-orm"; import { relations } from "drizzle-orm";
import { animeTable } from "./tables/anime"; import { anime } from "./tables/anime";
import { animeGenresTable } from "./tables/anime-genres"; import { animeGenres } from "./tables/anime-genres";
import { animeNamesTable } from "./tables/anime-names"; import { animeNames } from "./tables/anime-names";
import { animeSongLinksTable } from "./tables/anime-song-links"; import { animeSongLinks } from "./tables/anime-song-links";
import { animeTagsTable } from "./tables/anime-tags"; import { animeTags } from "./tables/anime-tags";
import { artistAltNamesTable } from "./tables/artist-alt-names"; import { artistAltNames } from "./tables/artist-alt-names";
import { artistGroupsTable } from "./tables/artist-groups"; import { artistGroups } from "./tables/artist-groups";
import { artistsTable } from "./tables/artists"; import { artists } from "./tables/artists";
import { genresTable } from "./tables/genres"; import { genres } from "./tables/genres";
import { groupAltNamesTable } from "./tables/group-alt-names"; import { groupAltNames } from "./tables/group-alt-names";
import { groupArtistMembersTable } from "./tables/group-artist-members"; import { groupArtistMembers } from "./tables/group-artist-members";
import { groupGroupMembersTable } from "./tables/group-group-members"; import { groupGroupMembers } from "./tables/group-group-members";
import { groupsTable } from "./tables/groups"; import { groups } from "./tables/groups";
import { songsTable } from "./tables/songs"; import { songs } from "./tables/songs";
import { tagsTable } from "./tables/tags"; import { tags } from "./tables/tags";
// ---------------------- // ----------------------
// Relations (optional but helpful for Drizzle queries) // Relations (optional but helpful for Drizzle queries)
// ---------------------- // ----------------------
export const animeRelations = relations(animeTable, ({ many }) => ({ export const animeRelations = relations(anime, ({ many }) => ({
names: many(animeNamesTable), names: many(animeNames),
genres: many(animeGenresTable), genres: many(animeGenres),
tags: many(animeTagsTable), tags: many(animeTags),
songLinks: many(animeSongLinksTable), songLinks: many(animeSongLinks),
})); }));
export const songRelations = relations(songsTable, ({ many }) => ({ export const songRelations = relations(songs, ({ many }) => ({
animeLinks: many(animeSongLinksTable), animeLinks: many(animeSongLinks),
})); }));
export const artistRelations = relations(artistsTable, ({ many }) => ({ export const artistRelations = relations(artists, ({ many }) => ({
inGroups: many(artistGroupsTable), inGroups: many(artistGroups),
altNames: many(artistAltNamesTable), altNames: many(artistAltNames),
groupMemberships: many(groupArtistMembersTable), groupMemberships: many(groupArtistMembers),
})); }));
export const groupRelations = relations(groupsTable, ({ many }) => ({ export const groupRelations = relations(groups, ({ many }) => ({
artists: many(artistGroupsTable), artists: many(artistGroups),
artistMembers: many(groupArtistMembersTable), artistMembers: many(groupArtistMembers),
groupMembers: many(groupGroupMembersTable), groupMembers: many(groupGroupMembers),
altNames: many(groupAltNamesTable), altNames: many(groupAltNames),
})); }));
export const animeNamesRelations = relations(animeNamesTable, ({ one }) => ({ export const animeNamesRelations = relations(animeNames, ({ one }) => ({
anime: one(animeTable, { anime: one(anime, {
fields: [animeNamesTable.annId], fields: [animeNames.annId],
references: [animeTable.annId], references: [anime.annId],
}), }),
})); }));
export const animeGenresRelations = relations(animeGenresTable, ({ one }) => ({ export const animeGenresRelations = relations(animeGenres, ({ one }) => ({
anime: one(animeTable, { anime: one(anime, {
fields: [animeGenresTable.annId], fields: [animeGenres.annId],
references: [animeTable.annId], references: [anime.annId],
}), }),
genre: one(genresTable, { genre: one(genres, {
fields: [animeGenresTable.genreName], fields: [animeGenres.genreName],
references: [genresTable.name], references: [genres.name],
}), }),
})); }));
export const animeTagsRelations = relations(animeTagsTable, ({ one }) => ({ export const animeTagsRelations = relations(animeTags, ({ one }) => ({
anime: one(animeTable, { anime: one(anime, {
fields: [animeTagsTable.annId], fields: [animeTags.annId],
references: [animeTable.annId], references: [anime.annId],
}), }),
tag: one(tagsTable, { tag: one(tags, {
fields: [animeTagsTable.tagName], fields: [animeTags.tagName],
references: [tagsTable.name], references: [tags.name],
}), }),
})); }));
export const animeSongLinksRelations = relations( export const animeSongLinksRelations = relations(animeSongLinks, ({ one }) => ({
animeSongLinksTable, anime: one(anime, {
({ one }) => ({ fields: [animeSongLinks.annId],
anime: one(animeTable, { references: [anime.annId],
fields: [animeSongLinksTable.annId],
references: [animeTable.annId],
}),
song: one(songsTable, {
fields: [animeSongLinksTable.annSongId],
references: [songsTable.annSongId],
}),
}), }),
); song: one(songs, {
fields: [animeSongLinks.annSongId],
references: [songs.annSongId],
}),
}));

View File

@@ -5,23 +5,23 @@ import {
sqliteTable, sqliteTable,
text, text,
} from "drizzle-orm/sqlite-core"; } from "drizzle-orm/sqlite-core";
import { animeTable } from "./anime"; import { anime } from "./anime";
import { genresTable } from "./genres"; import { genres } from "./genres";
/** /**
* Join table: Anime -> Genres * Join table: Anime -> Genres
* *
* Source: AmqAnimeSchema.genres (string[]) * Source: AmqAnimeSchema.genres (string[])
*/ */
export const animeGenresTable = sqliteTable( export const animeGenres = sqliteTable(
"anime_genres", "anime_genres",
{ {
annId: integer("ann_id") annId: integer("ann_id")
.notNull() .notNull()
.references(() => animeTable.annId, { onDelete: "cascade" }), .references(() => anime.annId, { onDelete: "cascade" }),
genreName: text("genre_name") genreName: text("genre_name")
.notNull() .notNull()
.references(() => genresTable.name, { onDelete: "cascade" }), .references(() => genres.name, { onDelete: "cascade" }),
}, },
(t) => ({ (t) => ({
pk: primaryKey({ pk: primaryKey({

View File

@@ -5,7 +5,7 @@ import {
text, text,
uniqueIndex, uniqueIndex,
} from "drizzle-orm/sqlite-core"; } from "drizzle-orm/sqlite-core";
import { animeTable } from "./anime"; import { anime } from "./anime";
/** /**
* Additional localized/alternative names for an anime. * Additional localized/alternative names for an anime.
@@ -14,14 +14,14 @@ import { animeTable } from "./anime";
* - language: "EN" | "JA" (per source) * - language: "EN" | "JA" (per source)
* - name: string * - name: string
*/ */
export const animeNamesTable = sqliteTable( export const animeNames = sqliteTable(
"anime_names", "anime_names",
{ {
id: integer("id").notNull().primaryKey({ autoIncrement: true }), id: integer("id").notNull().primaryKey({ autoIncrement: true }),
annId: integer("ann_id") annId: integer("ann_id")
.notNull() .notNull()
.references(() => animeTable.annId, { onDelete: "cascade" }), .references(() => anime.annId, { onDelete: "cascade" }),
/** "EN" | "JA" per source */ /** "EN" | "JA" per source */
language: text("language").notNull(), language: text("language").notNull(),

View File

@@ -4,24 +4,24 @@ import {
primaryKey, primaryKey,
sqliteTable, sqliteTable,
} from "drizzle-orm/sqlite-core"; } from "drizzle-orm/sqlite-core";
import { animeTable } from "./anime"; import { anime } from "./anime";
import { songsTable } from "./songs"; import { songs } from "./songs";
/** /**
* Join table: Anime <-> Songs links * Join table: Anime <-> Songs links
* *
* Source: AmqAnimeSchema.songLinks (AmqSongLink[]) * Source: AmqAnimeSchema.songLinks (AmqSongLink[])
*/ */
export const animeSongLinksTable = sqliteTable( export const animeSongLinks = sqliteTable(
"anime_song_links", "anime_song_links",
{ {
annId: integer("ann_id") annId: integer("ann_id")
.notNull() .notNull()
.references(() => animeTable.annId, { onDelete: "cascade" }), .references(() => anime.annId, { onDelete: "cascade" }),
annSongId: integer("ann_song_id") annSongId: integer("ann_song_id")
.notNull() .notNull()
.references(() => songsTable.annSongId, { onDelete: "cascade" }), .references(() => songs.annSongId, { onDelete: "cascade" }),
/** 1(OP) | 2(ED) | 3(INS) per SongLinkType */ /** 1(OP) | 2(ED) | 3(INS) per SongLinkType */
type: integer("type").notNull(), type: integer("type").notNull(),

View File

@@ -5,23 +5,23 @@ import {
sqliteTable, sqliteTable,
text, text,
} from "drizzle-orm/sqlite-core"; } from "drizzle-orm/sqlite-core";
import { animeTable } from "./anime"; import { anime } from "./anime";
import { tagsTable } from "./tags"; import { tags } from "./tags";
/** /**
* Join table: Anime -> Tags * Join table: Anime -> Tags
* *
* Source: AmqAnimeSchema.tags (string[]) * Source: AmqAnimeSchema.tags (string[])
*/ */
export const animeTagsTable = sqliteTable( export const animeTags = sqliteTable(
"anime_tags", "anime_tags",
{ {
annId: integer("ann_id") annId: integer("ann_id")
.notNull() .notNull()
.references(() => animeTable.annId, { onDelete: "cascade" }), .references(() => anime.annId, { onDelete: "cascade" }),
tagName: text("tag_name") tagName: text("tag_name")
.notNull() .notNull()
.references(() => tagsTable.name, { onDelete: "cascade" }), .references(() => tags.name, { onDelete: "cascade" }),
}, },
(t) => ({ (t) => ({
pk: primaryKey({ pk: primaryKey({

View File

@@ -8,7 +8,7 @@ import { index, integer, sqliteTable, text } from "drizzle-orm/sqlite-core";
* *
* Source: AmqAnimeSchema * Source: AmqAnimeSchema
*/ */
export const animeTable = sqliteTable( export const anime = sqliteTable(
"anime", "anime",
{ {
/** AMQ anime ID */ /** AMQ anime ID */

View File

@@ -5,7 +5,7 @@ import {
text, text,
uniqueIndex, uniqueIndex,
} from "drizzle-orm/sqlite-core"; } from "drizzle-orm/sqlite-core";
import { artistsTable } from "./artists"; import { artists } from "./artists";
/** /**
* Alternative names for artists. * Alternative names for artists.
@@ -14,7 +14,7 @@ import { artistsTable } from "./artists";
* *
* Interpreted as: artist `songArtistId` is also known as `name` (optionally via altSongArtistId link). * Interpreted as: artist `songArtistId` is also known as `name` (optionally via altSongArtistId link).
*/ */
export const artistAltNamesTable = sqliteTable( export const artistAltNames = sqliteTable(
"artist_alt_names", "artist_alt_names",
{ {
id: integer("id").notNull().primaryKey({ autoIncrement: true }), id: integer("id").notNull().primaryKey({ autoIncrement: true }),
@@ -24,7 +24,7 @@ export const artistAltNamesTable = sqliteTable(
*/ */
songArtistId: integer("song_artist_id") songArtistId: integer("song_artist_id")
.notNull() .notNull()
.references(() => artistsTable.songArtistId, { .references(() => artists.songArtistId, {
onDelete: "cascade", onDelete: "cascade",
}), }),
@@ -36,7 +36,7 @@ export const artistAltNamesTable = sqliteTable(
*/ */
altSongArtistId: integer("alt_song_artist_id") altSongArtistId: integer("alt_song_artist_id")
.notNull() .notNull()
.references(() => artistsTable.songArtistId, { .references(() => artists.songArtistId, {
onDelete: "cascade", onDelete: "cascade",
}), }),

View File

@@ -4,25 +4,25 @@ import {
primaryKey, primaryKey,
sqliteTable, sqliteTable,
} from "drizzle-orm/sqlite-core"; } from "drizzle-orm/sqlite-core";
import { artistsTable } from "./artists"; import { artists } from "./artists";
import { groupsTable } from "./groups"; import { groups } from "./groups";
/** /**
* Join table: Artist -> Groups membership * Join table: Artist -> Groups membership
* *
* Source: AmqArtistSchema.inGroups * Source: AmqArtistSchema.inGroups
*/ */
export const artistGroupsTable = sqliteTable( export const artistGroups = sqliteTable(
"artist_groups", "artist_groups",
{ {
songArtistId: integer("song_artist_id") songArtistId: integer("song_artist_id")
.notNull() .notNull()
.references(() => artistsTable.songArtistId, { .references(() => artists.songArtistId, {
onDelete: "cascade", onDelete: "cascade",
}), }),
songGroupId: integer("song_group_id") songGroupId: integer("song_group_id")
.notNull() .notNull()
.references(() => groupsTable.songGroupId, { onDelete: "cascade" }), .references(() => groups.songGroupId, { onDelete: "cascade" }),
}, },
(t) => ({ (t) => ({
pk: primaryKey({ pk: primaryKey({

View File

@@ -5,7 +5,7 @@ import { index, integer, sqliteTable, text } from "drizzle-orm/sqlite-core";
* *
* Source: AmqArtistSchema * Source: AmqArtistSchema
*/ */
export const artistsTable = sqliteTable( export const artists = sqliteTable(
"artists", "artists",
{ {
/** AMQ songArtistId */ /** AMQ songArtistId */

View File

@@ -5,7 +5,7 @@ import { index, sqliteTable, text } from "drizzle-orm/sqlite-core";
* *
* Source: AmqAnimeSchema.genres (string[]) * Source: AmqAnimeSchema.genres (string[])
*/ */
export const genresTable = sqliteTable( export const genres = sqliteTable(
"genres", "genres",
{ {
/** Primary key is the genre string itself */ /** Primary key is the genre string itself */

View File

@@ -5,7 +5,7 @@ import {
text, text,
uniqueIndex, uniqueIndex,
} from "drizzle-orm/sqlite-core"; } from "drizzle-orm/sqlite-core";
import { groupsTable } from "./groups"; import { groups } from "./groups";
/** /**
* Alternative names for groups. * Alternative names for groups.
@@ -16,7 +16,7 @@ import { groupsTable } from "./groups";
* `songGroupId` is effectively the *context group* the alias is associated with. * `songGroupId` is effectively the *context group* the alias is associated with.
* We persist it as `contextSongGroupId` to make the meaning explicit. * We persist it as `contextSongGroupId` to make the meaning explicit.
*/ */
export const groupAltNamesTable = sqliteTable( export const groupAltNames = sqliteTable(
"group_alt_names", "group_alt_names",
{ {
id: integer("id").notNull().primaryKey({ autoIncrement: true }), id: integer("id").notNull().primaryKey({ autoIncrement: true }),
@@ -26,7 +26,7 @@ export const groupAltNamesTable = sqliteTable(
*/ */
songGroupId: integer("song_group_id") songGroupId: integer("song_group_id")
.notNull() .notNull()
.references(() => groupsTable.songGroupId, { onDelete: "cascade" }), .references(() => groups.songGroupId, { onDelete: "cascade" }),
/** /**
* Context group the alias is associated with. * Context group the alias is associated with.
@@ -34,7 +34,7 @@ export const groupAltNamesTable = sqliteTable(
*/ */
contextSongGroupId: integer("context_song_group_id") contextSongGroupId: integer("context_song_group_id")
.notNull() .notNull()
.references(() => groupsTable.songGroupId, { onDelete: "cascade" }), .references(() => groups.songGroupId, { onDelete: "cascade" }),
name: text("name").notNull(), name: text("name").notNull(),
}, },

View File

@@ -4,23 +4,23 @@ import {
primaryKey, primaryKey,
sqliteTable, sqliteTable,
} from "drizzle-orm/sqlite-core"; } from "drizzle-orm/sqlite-core";
import { artistsTable } from "./artists"; import { artists } from "./artists";
import { groupsTable } from "./groups"; import { groups } from "./groups";
/** /**
* Join table: Group -> Artist members * Join table: Group -> Artist members
* *
* Source: AmqGroupSchema.artistMembers * Source: AmqGroupSchema.artistMembers
*/ */
export const groupArtistMembersTable = sqliteTable( export const groupArtistMembers = sqliteTable(
"group_artist_members", "group_artist_members",
{ {
songGroupId: integer("song_group_id") songGroupId: integer("song_group_id")
.notNull() .notNull()
.references(() => groupsTable.songGroupId, { onDelete: "cascade" }), .references(() => groups.songGroupId, { onDelete: "cascade" }),
songArtistId: integer("song_artist_id") songArtistId: integer("song_artist_id")
.notNull() .notNull()
.references(() => artistsTable.songArtistId, { .references(() => artists.songArtistId, {
onDelete: "cascade", onDelete: "cascade",
}), }),
}, },

View File

@@ -4,22 +4,22 @@ import {
primaryKey, primaryKey,
sqliteTable, sqliteTable,
} from "drizzle-orm/sqlite-core"; } from "drizzle-orm/sqlite-core";
import { groupsTable } from "./groups"; import { groups } from "./groups";
/** /**
* Join table: Group -> Group members * Join table: Group -> Group members
* *
* Source: AmqGroupSchema.groupMembers * Source: AmqGroupSchema.groupMembers
*/ */
export const groupGroupMembersTable = sqliteTable( export const groupGroupMembers = sqliteTable(
"group_group_members", "group_group_members",
{ {
songGroupId: integer("song_group_id") songGroupId: integer("song_group_id")
.notNull() .notNull()
.references(() => groupsTable.songGroupId, { onDelete: "cascade" }), .references(() => groups.songGroupId, { onDelete: "cascade" }),
memberSongGroupId: integer("member_song_group_id") memberSongGroupId: integer("member_song_group_id")
.notNull() .notNull()
.references(() => groupsTable.songGroupId, { onDelete: "cascade" }), .references(() => groups.songGroupId, { onDelete: "cascade" }),
}, },
(t) => ({ (t) => ({
pk: primaryKey({ pk: primaryKey({

View File

@@ -5,7 +5,7 @@ import { index, integer, sqliteTable, text } from "drizzle-orm/sqlite-core";
* *
* Source: AmqGroupSchema * Source: AmqGroupSchema
*/ */
export const groupsTable = sqliteTable( export const groups = sqliteTable(
"groups", "groups",
{ {
/** AMQ songGroupId */ /** AMQ songGroupId */

View File

@@ -5,15 +5,15 @@ import {
sqliteTable, sqliteTable,
text, text,
} from "drizzle-orm/sqlite-core"; } from "drizzle-orm/sqlite-core";
import { artistsTable } from "./artists"; import { artists } from "./artists";
import { groupsTable } from "./groups"; import { groups } from "./groups";
/** /**
* Core `songs` table. * Core `songs` table.
* *
* Source: AmqSongSchema * Source: AmqSongSchema
*/ */
export const songsTable = sqliteTable( export const songs = sqliteTable(
"songs", "songs",
{ {
/** AMQ annSongId (ANN song id) */ /** AMQ annSongId (ANN song id) */
@@ -44,31 +44,30 @@ export const songsTable = sqliteTable(
* These reference existing `artists` / `groups` rows. * These reference existing `artists` / `groups` rows.
*/ */
songArtistId: integer("song_artist_id").references( songArtistId: integer("song_artist_id").references(
() => artistsTable.songArtistId, () => artists.songArtistId,
{ onDelete: "set null" },
),
songGroupId: integer("song_group_id").references(
() => groupsTable.songGroupId,
{ onDelete: "set null" }, { onDelete: "set null" },
), ),
songGroupId: integer("song_group_id").references(() => groups.songGroupId, {
onDelete: "set null",
}),
/** /**
* Additional contributor ids (nullable in source) * Additional contributor ids (nullable in source)
*/ */
composerArtistId: integer("composer_artist_id").references( composerArtistId: integer("composer_artist_id").references(
() => artistsTable.songArtistId, () => artists.songArtistId,
{ onDelete: "set null" }, { onDelete: "set null" },
), ),
composerGroupId: integer("composer_group_id").references( composerGroupId: integer("composer_group_id").references(
() => groupsTable.songGroupId, () => groups.songGroupId,
{ onDelete: "set null" }, { onDelete: "set null" },
), ),
arrangerArtistId: integer("arranger_artist_id").references( arrangerArtistId: integer("arranger_artist_id").references(
() => artistsTable.songArtistId, () => artists.songArtistId,
{ onDelete: "set null" }, { onDelete: "set null" },
), ),
arrangerGroupId: integer("arranger_group_id").references( arrangerGroupId: integer("arranger_group_id").references(
() => groupsTable.songGroupId, () => groups.songGroupId,
{ onDelete: "set null" }, { onDelete: "set null" },
), ),
}, },

View File

@@ -5,7 +5,7 @@ import { index, sqliteTable, text } from "drizzle-orm/sqlite-core";
* *
* Source: AmqAnimeSchema.tags (string[]) * Source: AmqAnimeSchema.tags (string[])
*/ */
export const tagsTable = sqliteTable( export const tags = sqliteTable(
"tags", "tags",
{ {
/** Primary key is the tag string itself */ /** Primary key is the tag string itself */