Prisma explicit many to many field already defined - prisma

I have comics which has many writers, artists and characters and i'm trying to join everything together in prisma, but struggling to understand what I'm doing wrong.
Comic schema:
model comics {
uniqueCoverId String #id
name String
artists comic_contributors[] #relation("artists")
writers comic_contributors[] #relation("writers")
}
Artists schema
model comic_artists {
id String #id #default(uuid())
name String?
comics comic_contributors[]
}
Writers schema
model comic_writers {
id String #id #default(uuid())
name String?
comics comic_contributors[]
}
Intermediary table
model comic_contributors {
writer_id String #unique
writer comic_writers? #relation("writers", fields: [writer_id], references: [id])
artist_id String #unique
artist comic_artists? #relation("artists", fields: [artist_id], references: [id])
##id([writer_id, artist_id])
}
When I try to format prisma it returns the errors
error: Field "comics" is already defined on model "comic_contributors".
comicsUniqueCoverId String?
comics comics? #relation(fields: [comicsUniqueCoverId], references: [uniqueCoverId])
error: Field "comicsUniqueCoverId" is already defined on model "comic_contributors".
comics comics? #relation(fields: [comicsUniqueCoverId], references: [uniqueCoverId])
comicsUniqueCoverId String?
Any suggestions on what I'm doing wrong please?

You don't need to explicit a many-to-many relationship table, Prisma is doing it for you!
Prisma implicit many-to-many
You only have to say:
model comics {
uniqueCoverId String #id
name String
artists comic_artists[]
writers comic_writers[]
}
model comic_artists {
id String #id #default(uuid())
name String?
comics comics[]
}
model comic_writers {
id String #id #default(uuid())
name String?
comics comics[]
}
and Prisma will create hidden relationship tables as
_comic_artitsTocomics
column A -> comic_artits.id
column B -> comics.uniqueCoverId
_comic_writersTocomics
column A -> comic_writers.id
column B -> comics.uniqueCoverId
This allow you to do
const comic = await prisma.comics.findUnique({
where: {
uniqueCoverId: 'UniqueCoverId',
},
include: {
artists: true,
writers: true,
},
});
And comic will looks like
{
uniqueCoverId: 'UniqueCoverId',
name: 'Name',
artists: [
{ id: '210059e6-00ab-448c-aea9-b706251ade52', name: 'Artist1' },
{ id: '38cd8efa-2a66-4fe5-ad47-ec4f511647c0', name: 'Artist2' },
{ id: '86d6c908-f17b-4fbd-b0ee-c314f06aeaa9', name: 'Artist3' }
],
writers: [
{ id: '9cf97bf4-9c43-4579-924b-15a7f5dcb3f9', name: 'Writer1' },
{ id: 'afe6eb2d-6d69-44f2-a491-14827dc94e66', name: 'Writer2' },
{ id: 'cdac61cf-339d-460d-960b-6581fa2d7a57', name: 'Writer3' }
]
}
Prisma explicit many-to-many
But if you really need an explicit many-to-many relationship to add some data on the relation it will looks like this schema
model comics {
uniqueCoverId String #id
name String
artists comic_artistsOncomics[]
writers comic_writersOncomics[]
}
model comic_artists {
id String #id #default(uuid())
name String?
comics comic_artistsOncomics[]
}
model comic_artistsOncomics {
uniqueCoverId String
comic comics #relation(fields: [uniqueCoverId], references: [uniqueCoverId])
artistId String
artist comic_artists #relation(fields: [artistId], references: [id])
// Some data
##id([uniqueCoverId, artistId])
}
model comic_writers {
id String #id #default(uuid())
name String?
comics comic_writersOncomics[]
}
model comic_writersOncomics {
uniqueCoverId String
comic comics #relation(fields: [uniqueCoverId], references: [uniqueCoverId])
writerId String
writer comic_writers #relation(fields: [writerId], references: [id])
// Some data
##id([uniqueCoverId, writerId])
}
The previous findUnique will now looks like
const test = await prisma.comics.findUnique({
where: {
uniqueCoverId: 'uniqueCoverId',
},
include: {
artists: {
include: {
artist: true,
},
},
writers: {
include: {
writer: true,
},
},
},
});
And the result
{
uniqueCoverId: "UniqueCoverId",
name: "Name",
artists: [
{
uniqueCoverId: "UniqueCoverId",
artistId: "210059e6-00ab-448c-aea9-b706251ade52",
artist: {
id: "210059e6-00ab-448c-aea9-b706251ade52",
name: "Artist1",
},
},
{
uniqueCoverId: "UniqueCoverId",
artistId: "38cd8efa-2a66-4fe5-ad47-ec4f511647c0",
artist: {
id: "38cd8efa-2a66-4fe5-ad47-ec4f511647c0",
name: "Artist2",
},
},
{
uniqueCoverId: "UniqueCoverId",
artistId: "86d6c908-f17b-4fbd-b0ee-c314f06aeaa9",
artist: {
id: "86d6c908-f17b-4fbd-b0ee-c314f06aeaa9",
name: "Artist3",
},
},
],
writers: [
{
uniqueCoverId: "UniqueCoverId",
writerId: "9cf97bf4-9c43-4579-924b-15a7f5dcb3f9",
writer: {
id: "9cf97bf4-9c43-4579-924b-15a7f5dcb3f9",
name: "Writer1",
},
},
{
uniqueCoverId: "UniqueCoverId",
writerId: "afe6eb2d-6d69-44f2-a491-14827dc94e66",
writer: {
id: "afe6eb2d-6d69-44f2-a491-14827dc94e66",
name: "Writer2",
},
},
{
uniqueCoverId: "UniqueCoverId",
writerId: "cdac61cf-339d-460d-960b-6581fa2d7a57",
writer: {
id: "cdac61cf-339d-460d-960b-6581fa2d7a57",
name: "Writer3",
},
},
],
}
EDIT FOR YOU TO TEST
model comics {
id String #id #default(uuid())
uniqueCoverId String #unique
name String
artists comic_artists[]
writers comic_writers[]
}
model comic_artists {
id String #id #default(uuid())
name String?
comics comics[]
}
model comic_writers {
id String #id #default(uuid())
name String?
comics comics[]
}

Related

Can't create a nested many To many properties

I've been stuck on this problem for hours.
I'm trying to create an object linked by a many to many relationship.
However, I get this error when the function is invoked:
Unknown arg 0 in data.merchandises.create.0 for type ListingsMerchandisesCreateWithoutListingInput.
Argument merchandise for data.merchandises.create.merchandise is missing.
I don't know where that "0" comes from.
Thank you in advance for your help..
Full log of error:
Invalid `prisma[prismaModel].create()` invocation in
/app/node_modules/prisma-factory/dist/index.js:109:44
106 data = hooks.beforeCreate(data);
107 }
108 const prismaModel = (0, import_camel_case.camelCase)(modelName);
→ 109 let result = await prisma[prismaModel].create({
data: {
type: 'FOR_RENT',
name: 'consequatur',
merchandises: {
create: {
'0': {
merchandise: {
create: {
cosmetic: 'GOOD',
typology: 'NEW',
quantity: 76,
price: 80,
location: {
create: {
name: 'repellat',
line1: '600 Jerde Mews',
line2: 'Apt. 517',
line3: '79414 Lenore Harbor',
line4: 'placeat',
city: 'Port Astridshire',
postalCode: '47459-8067',
state: 'North Carolina',
country: 'Antigua and Barbuda',
other: 'iste'
}
},
user: {
create: {
email: 'Neoma41#gmail.com',
firstName: 'Casimir',
lastName: 'Kub'
}
},
product: {
create: {
name: 'Luxurious Steel Keyboard',
description: 'The Nagasaki Lander is the trademarked name of several series of Nagasaki sport bikes, that started with the 1984 ABC800J',
barcode: 'r0h7w9h1d',
barcodeType: 'EAN13',
brand: 'Bespoke',
model: 'Countach',
lenght: 25,
width: 57,
weight: 11,
capacity: 62,
impactUnit: 'UNIT',
manufacturingImpact: 31,
destructiveImpact: 81
}
}
}
}
},
+ merchandise: {
+ create?: MerchandiseCreateWithoutListingsInput | MerchandiseUncheckedCreateWithoutListingsInput,
+ connectOrCreate?: MerchandiseCreateOrConnectWithoutListingsInput,
+ connect?: MerchandiseWhereUniqueInput
+ }
}
}
}
})
Unknown arg `0` in data.merchandises.create.0 for type ListingsMerchandisesCreateWithoutListingInput.
Argument merchandise for data.merchandises.create.merchandise is missing.
The factory:
import { createFactory } from 'prisma-factory';
import { faker } from '#faker-js/faker';
import {
BarcodeType,
Cosmetic,
ImpactUnit,
Listing,
ListingType,
Prisma,
Typology,
} from '#prisma/client';
const DEFAULT_ATTRIBUTES = {
type: ListingType.FOR_RENT,
name: faker.lorem.word(),
merchandises: {
create: [
{
merchandise: {
create: {
cosmetic: Cosmetic.GOOD,
typology: Typology.NEW,
quantity: Number(faker.random.numeric(2)),
price: Number(faker.random.numeric(2)),
location: {
create: {
name: faker.lorem.word(),
line1: faker.address.streetAddress(),
line2: faker.address.secondaryAddress(),
line3: faker.address.streetAddress(),
line4: faker.lorem.word(),
city: faker.address.city(),
postalCode: faker.address.zipCode(),
state: faker.address.state(),
country: faker.address.country(),
other: faker.lorem.word(4),
},
},
user: {
create: {
email: faker.internet.email(),
firstName: faker.name.firstName(),
lastName: faker.name.lastName(),
},
},
product: {
create: {
name: faker.commerce.productName(),
description: faker.commerce.productDescription(),
barcode: faker.random.alphaNumeric(9),
barcodeType: BarcodeType.EAN13,
brand: faker.commerce.productAdjective(),
model: faker.vehicle.model(),
lenght: Number(faker.random.numeric(2)),
width: Number(faker.random.numeric(2)),
weight: Number(faker.random.numeric(2)),
capacity: Number(faker.random.numeric(2)),
impactUnit: ImpactUnit.UNIT,
manufacturingImpact: Number(faker.random.numeric(2)),
destructiveImpact: Number(faker.random.numeric(2)),
},
},
},
},
},
],
},
};
export const ListingFactory = createFactory<Prisma.ListingCreateInput, Listing>(
'listing',
DEFAULT_ATTRIBUTES,
);
concerning schema:
model Listing {
id Int #id #default(autoincrement())
type ListingType
name String
merchandises ListingsMerchandises[]
availability Availability?
category Category? #relation(fields: [categoryId], references: [id])
categoryId Int?
createdAt DateTime #default(now())
updatedAt DateTime #updatedAt
deletedAt DateTime?
}
model Merchandise {
id Int #id #default(autoincrement())
cosmetic Cosmetic
typology Typology
quantity Int
price Int
productId Int
product Product #relation(fields: [productId], references: [id])
userId Int
user User #relation(fields: [userId], references: [id])
locationId Int
location Location? #relation(fields: [locationId], references: [id])
listings ListingsMerchandises[]
createdAt DateTime #default(now())
updatedAt DateTime #updatedAt
deletedAt DateTime?
}
model ListingsMerchandises {
listingId Int
listing Listing #relation(fields: [listingId], references: [id])
merchandiseId Int
merchandise Merchandise #relation(fields: [merchandiseId], references: [id])
##id([listingId, merchandiseId])
}
I tried several formats, I even tried a connect but without success.
No type errors are thrown

Get all related models for Prisma many-to-many relation

I have the following Prisma schema:
model User {
id String #id #default(cuid())
name String?
email String? #unique
emailVerified DateTime?
image String?
accounts Account[]
sessions Session[]
joinedEvents Event[]
eventParticiants EventParticiants[]
}
model Event {
id String #id #default(cuid())
title String
description String
code String
num_tasks Int
num_beacons Int
createdAt DateTime #default(now())
updatedAt DateTime #updatedAt
owner User #relation(fields: [ownerId], references: [id])
ownerId String
eventParticiants EventParticiants[]
tasks Tasks[]
beacons Beacons[]
}
model EventParticiants {
event Event #relation(fields: [eventId], references: [id])
eventId String
user User #relation(fields: [userId], references: [id])
userId String
createdAt DateTime #default(now())
updatedAt DateTime #updatedAt
##id([eventId, userId])
}
Each user can join multiple events and each event can be joined by multiple users. This is mediated by a middle table.
I would like to return all the events for a user but this does not seem to be working:
const events = await prisma.user.findMany({
where: { owner: { email: session.user.email } },
select: {
eventParticiants: {
}
},
orderBy: { createdAt: "desc" },
});
You need to add another level of select or include to go from eventParticipants to events:
const events = await prisma.user.findMany({
where: { owner: { email: session.user.email } },
select: {
eventParticiants: {
include: {
event: true
}
}
},
orderBy: { createdAt: "desc" },
});

PRISMA - How to filter the relation nested after m2m relation?

How to filter a relation nested after m2m relation?
Unfortunately as for 2022-11 in Prisma documentation there isn't any info about that.
Schema.prisma:
model Entity {
id Int #id #default(autoincrement())
name String
service_entity_m2m ServiceEntityM2m[]
}
model Service {
id Int #id #default(autoincrement())
service_entity_m2m ServiceEntityM2m[]
}
model ServiceEntityM2m {
id Int #id #default(autoincrement())
entity_id Int
entity Entity #relation(fields: [entity_id], references: [id])
service_id Int
service Service #relation(fields: [service_id], references: [id])
}
Schema.graphql:
type Entity {
id: ID!
name: String
}
type Service {
id: ID!
service_entity_m2m: [ServiceEntityM2m]
}
type ServiceEntityM2m {
id: ID!
entity: Entity!
service: Service!
}
seeder:
// services
{
id: 1
}
// service_entity_m2m:
{
id: 1,
service_id: 1,
entity_id: 1
},
{
id: 2,
service_id: 1,
entity_id: 2
}
// entities
{
id: 1,
name: "XYZ"
},
{
id: 2,
name: "Test"
}
This code:
context.prisma.service.findMany({
where: { service_entity_m2m: { some: { entity: { name: { contains: "Test" } } } } },
include: { service_entity_m2m: { include: { entity: true } } },
})
returns both entities:
[
{
id: 1,
service_entity_m2m:
[
{
id: 1,
entity: {
id: 1,
name: "XYZ"
}
},
{
id: 2,
entity: {
id: 2,
name: "Test"
}
}
]
}
]
How to write a query that is going to return only one entity with the name "Test": ?
[
{
id: 1,
service_entity_m2m:
[
{
id: 2,
entity: {
id: 2,
name: "Test"
}
}
]
}
]

How to connect a many to many relationship using Prisma

I am trying to create and connect a record in a Prisma many to many relationship, but the connection part is not working for me.
Here are my Prisma models:
model Ingredient {
id Int #id #default(autoincrement())
name String
createdAt DateTime #default(now())
calories Int
protein Int
fat Int
carbs Int
netCarbs Int
metricQuantity Int
metricUnit String
imperialQuantity Int
imperialUnit String
recipes IngredientsOnRecipes[]
categories CategoriesOnIngredients[]
}
model IngredientCategory {
id Int #id #default(autoincrement())
name String
ingredients CategoriesOnIngredients[]
}
model CategoriesOnIngredients {
ingredient Ingredient #relation(fields: [ingredientId], references: [id])
ingredientId Int // relation scalar field (used in the `#relation` attribute above)
ingredientCategory IngredientCategory #relation(fields: [ingredientCategoryId], references: [id])
ingredientCategoryId Int // relation scalar field (used in the `#relation` attribute above)
assignedAt DateTime #default(now())
##id([ingredientId, ingredientCategoryId])
}
Here is the primsa query I am running:
const ingredient = await prisma.ingredient.create({
data: {
name: title,
metricQuantity: parseInt(quantityMetric),
metricUnit: unitMetric,
imperialQuantity: parseInt(quantityImperial),
imperialUnit: unitImperial,
calories: parseInt(calories),
netCarbs: parseInt(netCarbs),
carbs: parseInt(carbs),
protein: parseInt(protein),
fat: parseInt(fat),
categories: {
ingredientcategory: {
connect: { id: parseInt(categoryId) },
},
},
},
});
Creating a new ingredient works perfectly, but when I add this section:
categories: {
ingredientcategory: {
connect: { id: parseInt(categoryId) },
},
},
I get the following error:
Unknown arg ingredientcategory in data.categories.ingredientcategory for type CategoriesOnIngredientsCreateNestedManyWithoutIngredientInput. Did you mean createMany? Available args:
type CategoriesOnIngredientsCreateNestedManyWithoutIngredientInput {
create?: CategoriesOnIngredientsCreateWithoutIngredientInput | List | CategoriesOnIngredientsUncheckedCreateWithoutIngredientInput | List
connectOrCreate?: CategoriesOnIngredientsCreateOrConnectWithoutIngredientInput | List
createMany?: CategoriesOnIngredientsCreateManyIngredientInputEnvelope
connect?: CategoriesOnIngredientsWhereUniqueInput | List
}
You can try executing the following:
const { PrismaClient } = require('#prisma/client')
const prisma = new PrismaClient()
const saveData = async () => {
const ingredient = await prisma.ingredient.create({
data: {
name: 'ingredient1',
categories: {
create: {
ingredientCategory: {
create: {
name: 'category1',
},
}
}
},
},
select: {
id: true,
name: true,
categories: {
select: {
ingredientId: true,
ingredientCategory: true,
}
},
},
});
console.log(JSON.stringify(ingredient, null, 2));
}
saveData()
And you will have the following:
I managed to get it to work, I had missed create:{} from my Prisma query.
const ingredient = await prisma.ingredient.create({
data: {
name: title,
metricQuantity: parseInt(quantityMetric),
metricUnit: unitMetric,
imperialQuantity: parseInt(quantityImperial),
imperialUnit: unitImperial,
calories: parseInt(calories),
netCarbs: parseInt(netCarbs),
carbs: parseInt(carbs),
protein: parseInt(protein),
fat: parseInt(fat),
categories: {
create: {
ingredientCategory: {
connect: { id: parseInt(categoryId) },
},
},
},
},
});

findUnique query returns null for array fields

I read the Prisma Relations documentation and it fixed my findMany query which is able to return valid data but I'm getting inconsistent results with findUnique.
Schema
model User {
id Int #id #default(autoincrement())
fname String
lname String
email String
password String
vehicles Vehicle[]
}
model Vehicle {
id Int #id #default(autoincrement())
vin String #unique
model String
make String
drivers User[]
}
Typedefs
const typeDefs = gql'
type User {
id: ID!
fname: String
lname: String
email: String
password: String
vehicles: [Vehicle]
}
type Vehicle {
id: ID!
vin: String
model: String
make: String
drivers: [User]
}
type Mutation {
post(id: ID!, fname: String!, lname: String!): User
}
type Query {
users: [User]
user(id: ID!): User
vehicles: [Vehicle]
vehicle(vin: String): Vehicle
}
'
This one works
users: async (_, __, context) => {
return context.prisma.user.findMany({
include: { vehicles: true}
})
},
However, for some reason the findUnique version will not resolve the array field for "vehicles"
This one doesn't work
user: async (_, args, context) => {
const id = +args.id
return context.prisma.user.findUnique({ where: {id} },
include: { vehicles: true}
)
},
This is what it returns
{
"data": {
"user": {
"id": "1",
"fname": "Jess",
"lname": "Potato",
"vehicles": null
}
}
}
I was reading about fragments and trying to find documentation on graphql resolvers but I haven't found anything relevant that can solve this issue.
Any insight would be appreciated! Thanks!
You need to fix the arguments passed to findUnique. Notice the arrangement of the { and }.
Change
return context.prisma.user.findUnique({ where: { id } },
// ^
include: { vehicles: true}
)
to
return context.prisma.user.findUnique({
where: { id },
include: { vehicles: true }
})