🚧 wip: update bulk insert producer in media

This commit is contained in:
2026-05-31 12:56:12 +07:00
parent 1694035dc4
commit 0057f0c63b
4 changed files with 120 additions and 88 deletions

View File

@ -0,0 +1,74 @@
import { Prisma } from "@prisma/client";
import { GenreOrProducer, MediaFullInfoResponse } from "../types/mediaFullInfo.type";
import { SystemAccountId } from "../../../config/account/system";
import { status } from "elysia";
export const bulkInsertMediaProducerStudioLicensorRepository = async (
tx: Prisma.TransactionClient,
media_id: string,
payload: (GenreOrProducer & { status: "producer" | "licensor" | "studio" })[],
) => {
await tx.producer.createMany({
data: payload.map((p) => ({
mal_id: p.mal_id,
type: p.type,
name: p.name,
url: p.url,
created_by_id: SystemAccountId,
})),
skipDuplicates: true,
});
const insertedProducers = (
await tx.producer.findMany({
where: {
mal_id: {
in: payload.map((p) => p.mal_id),
},
},
select: {
id: true,
mal_id: true,
},
})
).map((producer) => {
const statusProducer = payload.find((p) => p.mal_id === producer.mal_id)?.status;
return {
id: producer.id,
mal_id: producer.mal_id,
status: statusProducer,
};
});
await tx.mediaProducer.createMany({
data: insertedProducers
.filter((p) => p.status === "producer")
.map((producer) => ({
media_id,
producer_id: producer.id,
})),
skipDuplicates: true,
});
await tx.mediaLicensor.createMany({
data: insertedProducers
.filter((p) => p.status === "licensor")
.map((producer) => ({
media_id,
licensor_id: producer.id,
})),
skipDuplicates: true,
});
await tx.mediaStudio.createMany({
data: insertedProducers
.filter((p) => p.status === "studio")
.map((producer) => ({
media_id,
studio_id: producer.id,
})),
skipDuplicates: true,
});
return insertedProducers;
};

View File

@ -1,69 +0,0 @@
import { SystemAccountId } from "../../../config/account/system";
import { generateSlug } from "../../../helpers/characters/generateSlug";
import { generateUUIDv7 } from "../../../helpers/databases/uuidv7";
import { AppError } from "../../../helpers/error/instances/app";
import { prisma } from "../../../utils/databases/prisma/connection";
import { MediaFullInfoResponse } from "../types/mediaFullInfo.type";
/**
* Studios Insertion
*
* This section manages the insertion of studios associated with the media.
* It processes each studio listed in the media data, generating a slug for
* each and performing an upsert operation to either create or update the
* studio record in the database. The IDs of the inserted or updated studios
* are collected for later association with the media.
*
* @param data - The full media data containing studios information.
* @returns An array of IDs of the inserted or updated studios.
*/
export const bulkInsertStudiosRepository = async (
data: MediaFullInfoResponse,
) => {
try {
const studioIds: string[] = [];
for (const studio of data.data.studios) {
const slug = (await generateSlug(studio.name)) as string;
const studioPayload = {
name: studio.name,
malId: studio.mal_id,
linkAbout: studio.url,
createdBy: SystemAccountId,
slug,
};
const insertedStudio = await prisma.studio.upsert({
where: { slug },
create: {
id: generateUUIDv7(),
...studioPayload,
},
update: studioPayload,
select: { id: true },
});
studioIds.push(insertedStudio.id);
}
for (const studio of data.data.producers) {
const slug = (await generateSlug(studio.name)) as string;
const studioPayload = {
name: studio.name,
malId: studio.mal_id,
linkAbout: studio.url,
createdBy: SystemAccountId,
slug,
};
const insertedStudio = await prisma.studio.upsert({
where: { slug },
create: {
id: generateUUIDv7(),
...studioPayload,
},
update: studioPayload,
select: { id: true },
});
studioIds.push(insertedStudio.id);
}
return studioIds;
} catch (error) {
throw new AppError(500, "Failed to insert studios", error);
}
};

View File

@ -4,6 +4,7 @@ import { prisma } from "../../../utils/databases/prisma/connection";
import { generateUUIDv7 } from "../../../helpers/databases/uuidv7"; import { generateUUIDv7 } from "../../../helpers/databases/uuidv7";
import { MediaFullInfoResponse } from "../types/mediaFullInfo.type"; import { MediaFullInfoResponse } from "../types/mediaFullInfo.type";
import { SystemAccountId } from "../../../config/account/system"; import { SystemAccountId } from "../../../config/account/system";
import { bulkInsertMediaProducerStudioLicensorRepository } from "./bulkInsertMediaProducerStudioLicensor.repository";
/** /**
* Media Payload Construction and Upsert * Media Payload Construction and Upsert
@ -109,10 +110,41 @@ export const InsertMediaRepository = async ({ payload }: { payload: MediaFullInf
broadcast_day: payload.broadcast.day, broadcast_day: payload.broadcast.day,
}; };
return await prisma.media.upsert({ const producerPayload = [
...payload.producers.map((producer) => ({
mal_id: producer.mal_id,
type: producer.type,
name: producer.name,
url: producer.url,
status: "producer" as const,
})),
...payload.licensors.map((licensor) => ({
mal_id: licensor.mal_id,
type: licensor.type,
name: licensor.name,
url: licensor.url,
status: "licensor" as const,
})),
...payload.studios.map((studio) => ({
mal_id: studio.mal_id,
type: studio.type,
name: studio.name,
url: studio.url,
status: "studio" as const,
})),
];
prisma.$transaction(async (tx) => {
const media = await tx.media.upsert({
where: { mal_id: payload.mal_id }, where: { mal_id: payload.mal_id },
create: constructMediaPayload, create: constructMediaPayload,
update: constructMediaPayload, update: constructMediaPayload,
select: {
id: true,
},
});
await bulkInsertMediaProducerStudioLicensorRepository(tx, media.id, producerPayload);
}); });
} catch (error) { } catch (error) {
throw new AppError(500, "Failed to insert media", error); throw new AppError(500, "Failed to insert media", error);

View File

@ -33,13 +33,13 @@ interface Data {
season: media_season; season: media_season;
year: number; year: number;
broadcast: Broadcast; broadcast: Broadcast;
producers: Genre[]; producers: GenreOrProducer[];
licensors: unknown[]; licensors: GenreOrProducer[];
studios: Genre[]; studios: GenreOrProducer[];
genres: Genre[]; genres: GenreOrProducer[];
explicit_genres: unknown[]; explicit_genres: GenreOrProducer[];
themes: Genre[]; themes: GenreOrProducer[];
demographics: unknown[]; demographics: GenreOrProducer[];
relations: Relation[]; relations: Relation[];
theme: Theme; theme: Theme;
external: External[]; external: External[];
@ -76,18 +76,13 @@ interface External {
url: string; url: string;
} }
interface Genre { export interface GenreOrProducer {
mal_id: number; mal_id: number;
type: Type; type: string;
name: string; name: string;
url: string; url: string;
} }
enum Type {
Anime = "anime",
Manga = "manga",
}
interface Image { interface Image {
image_url: string; image_url: string;
small_image_url: string; small_image_url: string;
@ -96,7 +91,7 @@ interface Image {
interface Relation { interface Relation {
relation: string; relation: string;
entry: Genre[]; entry: GenreOrProducer[];
} }
interface Theme { interface Theme {