Tengo una base de datos con varias tablas que con frecuencia deben consultarse con LEFT JOIN para que los resultados contengan datos agregados de otras tablas. Fragmento de mi esquema Prisma:
model posts { id Int @id @unique @default(autoincrement()) user_id Int movie_id Int @unique title String @db.Text description String? @db.Text tags Json? created_at DateTime @default(now()) @db.DateTime(0) image String? @default("https://picsum.photos/400/600/?blur=10") @db.VarChar(256) year Int submitted_by String @db.Text tmdb_rating Decimal? @default(0.0) @db.Decimal(3, 1) tmdb_rating_count Int? @default(0) } model ratings { id Int @unique @default(autoincrement()) @db.UnsignedInt entry_id Int @db.UnsignedInt user_id Int @db.UnsignedInt rating Int @default(0) @db.UnsignedTinyInt created_at DateTime @default(now()) @db.DateTime(0) updated_at DateTime? @db.DateTime(0) @@id([entry_id, user_id]) } Si quisiera devolver la calificación promedio al consultar posts , podría usar una consulta como:
SELECT p.*, ROUND(AVG(rt.rating), 1) AS user_rating FROM posts AS p LEFT JOIN ratings AS rt ON rt.entry_id = p.id GROUP BY p.id; No estoy exactamente seguro de cómo/si puedo lograr algo similar con Prisma, porque tal como está ahora, parece que esto requeriría dos consultas separadas, lo cual no es óptimo porque a veces se necesitan 2 o 3 uniones. o SELECT s de otras tablas.
¿Cómo puedo hacer una consulta/modelo/algo en Prisma para lograr lo anterior?
¡Sí, esto es posible con Prisma!. Para que esto funcione, debe especificar en su archivo "schema.prisma" cómo se relacionan los modelos entre sí. De esa forma, la generación de código establecerá las posibles consultas/operaciones.
Cámbialo por esto:
model Post { id Int @id @unique @default(autoincrement()) @map("id") userId Int @map("user_id") movieId Int @unique @map("movie_id") title String @map("title") @db.Text description String? @map("description") @db.Text tags Json? @map("tags") createdAt DateTime @default(now()) @map("created_at") @db.DateTime(0) image String? @default("https://picsum.photos/400/600/?blur=10") @map("image") @db.VarChar(256) year Int @map("year") submittedBy String @map("submitted_by") @db.Text tmdbRating Decimal? @default(0.0) @map("tmdb_rating") @db.Decimal(3, 1) tmdbRatingCount Int? @default(0) @map("tmdb_rating_count") ratings Rating[] @@map("posts") } model Rating { id Int @unique @default(autoincrement()) @map("id") @db.UnsignedInt userId Int @map("user_id") @db.UnsignedInt rating Int @default(0) @map("rating") @db.UnsignedTinyInt entryId Int entry Post @relation(fields: [entryId], references: [id]) createdAt DateTime @default(now()) @map("created_a") @db.DateTime(0) updatedAt DateTime? @map("updated_a") @db.DateTime(0) @@id([entryId, userId]) @@map("ratings") }Nota: siga las convenciones de nomenclatura (forma singular, PascalCase) . Hice esos cambios por ti en el esquema anterior. @@map le permite establecer el nombre que usa en sus tablas de base de datos.
Luego, después de generar el cliente, obtendrá acceso a las operaciones relacionales.
// All posts with ratings data const postsWithRatings = await prisma.post.findMany({ include: { // Here you can keep including data from other models ratings: true }, // you can also "select" specific properties }); // Calculate on your API const ratedPosts = postsWithRatings.map( post => { const ratingsCount = post.ratings.length; const ratingsTotal = post.ratings.reduce((acc, b) => acc + b.rating, 0) return { ...post, userRating: ratingsTotal / ratingsCount } }) // OR... // Get avg from db const averages = await prisma.rating.groupBy({ by: ["entryId"], _avg: { rating: true }, orderBy: { entryId: "desc" } }) // Get just posts const posts = await prisma.post.findMany({ orderBy: { id: "desc" } }); // then match the ratings with posts const mappedRatings = posts.map( (post, idx) => { return { ...post, userRating: averages[idx]._avg.rating } })También puede crear una clase con un método para hacerlo más fácil. Pero le recomiendo encarecidamente que implemente GraphQL en su API . De esa manera, puede agregar un campo virtual dentro de su tipo de publicación. Cada vez que se solicite una publicación sola o en una lista, se calculará el promedio. De la misma manera, tendría la flexibilidad de solicitar datos de otros modelos y las "UNIDAS" se manejarán por usted automáticamente.
Por último, pero no menos importante, si alguna vez desea realizar muchas consultas al mismo tiempo, puede aprovechar las transacciones de Prisma .