db: improve genre and tag handling

This commit is contained in:
2026-02-05 02:11:08 -08:00
parent e24fb58d4e
commit 01f3cfef45
2 changed files with 67 additions and 16 deletions

View File

@@ -28,11 +28,13 @@ import {
artistAltNamesTable, artistAltNamesTable,
artistGroupsTable, artistGroupsTable,
artistsTable, artistsTable,
genresTable,
groupAltNamesTable, groupAltNamesTable,
groupArtistMembersTable, groupArtistMembersTable,
groupGroupMembersTable, groupGroupMembersTable,
groupsTable, groupsTable,
songsTable, songsTable,
tagsTable,
} from "./schema"; } from "./schema";
/** /**
@@ -428,11 +430,18 @@ export async function importAmqData(
// genres // genres
if (a.genres.length) { if (a.genres.length) {
// Ensure lookup rows exist (string PK)
db.insert(genresTable)
.values(a.genres.map((g) => ({ name: g })))
.onConflictDoNothing()
.run();
// Insert relations
db.insert(animeGenresTable) db.insert(animeGenresTable)
.values( .values(
a.genres.map((g) => ({ a.genres.map((g) => ({
annId: a.annId, annId: a.annId,
genre: g, genreName: g,
})), })),
) )
.run(); .run();
@@ -440,11 +449,18 @@ export async function importAmqData(
// tags // tags
if (a.tags.length) { if (a.tags.length) {
// Ensure lookup rows exist (string PK)
db.insert(tagsTable)
.values(a.tags.map((t) => ({ name: t })))
.onConflictDoNothing()
.run();
// Insert relations
db.insert(animeTagsTable) db.insert(animeTagsTable)
.values( .values(
a.tags.map((t) => ({ a.tags.map((t) => ({
annId: a.annId, annId: a.annId,
tag: t, tagName: t,
})), })),
) )
.run(); .run();

View File

@@ -59,9 +59,9 @@ export const animeTable = sqliteTable(
insertCount: integer("insert_count").notNull(), insertCount: integer("insert_count").notNull(),
}, },
(t) => ({ (t) => ({
aniListIndex: uniqueIndex("anime_anilist_id_uq").on(t.aniListId), aniListIndex: index("anime_anilist_id_uq").on(t.aniListId),
malIndex: uniqueIndex("anime_mal_id_uq").on(t.malId), malIndex: index("anime_mal_id_uq").on(t.malId),
kitsuIndex: uniqueIndex("anime_kitsu_id_uq").on(t.kitsuId), kitsuIndex: index("anime_kitsu_id_uq").on(t.kitsuId),
}), }),
); );
@@ -279,38 +279,65 @@ export const animeNamesTable = sqliteTable(
}), }),
); );
export const genresTable = sqliteTable(
"genres",
{
/** Primary key is the genre string itself */
name: text("name").notNull().primaryKey(),
},
(t) => ({
nameIndex: index("genres_name_idx").on(t.name),
}),
);
export const animeGenresTable = sqliteTable( export const animeGenresTable = sqliteTable(
"anime_genres", "anime_genres",
{ {
id: integer("id").notNull().primaryKey({ autoIncrement: true }),
annId: integer("ann_id") annId: integer("ann_id")
.notNull() .notNull()
.references(() => animeTable.annId, { onDelete: "cascade" }), .references(() => animeTable.annId, { onDelete: "cascade" }),
genre: text("genre").notNull(), genreName: text("genre_name")
.notNull()
.references(() => genresTable.name, { onDelete: "cascade" }),
}, },
(t) => ({ (t) => ({
pk: primaryKey({
name: "anime_genres_pk",
columns: [t.annId, t.genreName],
}),
animeIndex: index("anime_genres_ann_id_idx").on(t.annId), animeIndex: index("anime_genres_ann_id_idx").on(t.annId),
genreIndex: index("anime_genres_genre_idx").on(t.genre), genreIndex: index("anime_genres_genre_name_idx").on(t.genreName),
uniquePerAnime: uniqueIndex("anime_genres_ann_genre_uq").on( }),
t.annId, );
t.genre,
), export const tagsTable = sqliteTable(
"tags",
{
/** Primary key is the tag string itself */
name: text("name").notNull().primaryKey(),
},
(t) => ({
nameIndex: index("tags_name_idx").on(t.name),
}), }),
); );
export const animeTagsTable = sqliteTable( export const animeTagsTable = sqliteTable(
"anime_tags", "anime_tags",
{ {
id: integer("id").notNull().primaryKey({ autoIncrement: true }),
annId: integer("ann_id") annId: integer("ann_id")
.notNull() .notNull()
.references(() => animeTable.annId, { onDelete: "cascade" }), .references(() => animeTable.annId, { onDelete: "cascade" }),
tag: text("tag").notNull(), tagName: text("tag_name")
.notNull()
.references(() => tagsTable.name, { onDelete: "cascade" }),
}, },
(t) => ({ (t) => ({
pk: primaryKey({
name: "anime_tags_pk",
columns: [t.annId, t.tagName],
}),
animeIndex: index("anime_tags_ann_id_idx").on(t.annId), animeIndex: index("anime_tags_ann_id_idx").on(t.annId),
tagIndex: index("anime_tags_tag_idx").on(t.tag), tagIndex: index("anime_tags_tag_name_idx").on(t.tagName),
uniquePerAnime: uniqueIndex("anime_tags_ann_tag_uq").on(t.annId, t.tag),
}), }),
); );
@@ -387,6 +414,10 @@ export const animeGenresRelations = relations(animeGenresTable, ({ one }) => ({
fields: [animeGenresTable.annId], fields: [animeGenresTable.annId],
references: [animeTable.annId], references: [animeTable.annId],
}), }),
genre: one(genresTable, {
fields: [animeGenresTable.genreName],
references: [genresTable.name],
}),
})); }));
export const animeTagsRelations = relations(animeTagsTable, ({ one }) => ({ export const animeTagsRelations = relations(animeTagsTable, ({ one }) => ({
@@ -394,6 +425,10 @@ export const animeTagsRelations = relations(animeTagsTable, ({ one }) => ({
fields: [animeTagsTable.annId], fields: [animeTagsTable.annId],
references: [animeTable.annId], references: [animeTable.annId],
}), }),
tag: one(tagsTable, {
fields: [animeTagsTable.tagName],
references: [tagsTable.name],
}),
})); }));
export const animeSongLinksRelations = relations( export const animeSongLinksRelations = relations(