Prisma Many-to-Many Relation - postgresql

model Students {
studentId Int #id #default(autoincrement())
user User? #relation(fields: [userId], references: [id], onDelete: Cascade, onUpdate: Cascade)
userId Int? #unique
classCode String?
courses Course[]
}
model Course {
courseId Int #id #default(autoincrement())
courseName String
courseDescription String?
courseInstructor String
classes Classes[]
students Students[]
}
I have data in both tables seperately. I do not want to insert data in one and connect it to other using implicit relation. I just want to connect them in their implicit model by inserting both foreign key in that table by create or insert query using prisma.
for now their implicit created model is empty.
I want to know the query for that in prisma client.
I have made the relations between two models of many to many. I have data in both tables separately and their implicit model in empty. I just want to add the foreign in that table by myself to connect them. I want to know the query for that in prisma client.

What you need here is connect and create. based on prisma documantation you can create query like this.
To connect a student and many course :
await prisma.student.create({
data: {
userId: 1,
classCode: "2"
Course: { connect: [{ courseName: 'programming', courseDescription: "desc", courseInstructor: "Mr/Mrs" }, { courseName: 'development', courseDescription: "desc", courseInstructor: "Mr/Mrs" }] },
},
})
To create a student and many course :
await prisma.student.create({
data: {
userId: 1,
classCode: "2"
Course: { connect: [{ courseName: 'programming', courseDescription: "desc", courseInstructor: "Mr/Mrs" }, { courseName: 'development', courseDescription: "desc", courseInstructor: "Mr/Mrs" }] },
},
})
or you can use connectOrCreate
Reference : https://www.prisma.io/docs/guides/database/troubleshooting-orm/help-articles/working-with-many-to-many-relations

Related

Prisma explicit many to many field already defined

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[]
}

How to filter on relation in Prisma ORM

I am working currently on a course service. Users have the possibility to register and deregister for courses. The entire system is built in a microservice architecture, which means that users are managed by another service. Therefore, the data model of the course service looks like this:
model course {
id Int #id #default(autoincrement())
orderNumber Int #unique
courseNumber String #unique #db.VarChar(255)
courseName String #db.VarChar(255)
courseOfficer String #db.VarChar(255)
degree String #db.VarChar(255)
ectCount Int
faculty String #db.VarChar(255)
isWinter Boolean #default(false)
isSummer Boolean #default(false)
courseDescription String? #db.VarChar(255)
enrollmentCourse enrollmentCourse[]
}
model enrollmentCourse {
id Int #id #default(autoincrement())
userId String #db.VarChar(1024)
course course #relation(fields: [courseId], references: [id])
courseId Int
}
I want to find all the courses in which a certain user has enrolled.
I have written 2 queries. One goes over the courses and tries to filter on the enrollmentCourse. However, this one does not work and I get all the courses back. Whereas the second one goes over the enrollmentCourse and then uses a mapping to return the courses. This works, but I don't like this solution and would prefer the 1st query if it worked:
(I have used this guide in order to write the first query: here)
const result1 = await this.prisma.course.findMany({
where: { enrollmentCourse: { every: { userId: user.id } } },
include: { enrollmentCourse: true }
});
console.log('Test result 1: ');
console.log(result1);
const result2 = await this.prisma.enrollmentCourse.findMany({
where: { userId: user.id },
include: { course: { include: { enrollmentCourse: true } } }
});
console.log('Test result 2: ');
console.log(result2.map((enrollment) => enrollment.course));
If now the user is not enrolled in a course the result of both queries are:
Test result 1:
[
{
id: 2,
orderNumber: 1,
courseNumber: 'test123',
courseName: 'testcourse',
courseOfficer: 'testcontact',
degree: 'Bachelor',
ectCount: 5,
faculty: 'testfaculty',
isWinter: true,
isSummer: false,
courseDescription: 'test.pdf',
enrollmentCourse: []
}
]
Test result 2:
[]
If now the user has enrolled courses it looks like this:
Test result 1:
[
{
id: 2,
orderNumber: 1,
courseNumber: 'test123',
courseName: 'testcourse',
courseOfficer: 'testcontact',
degree: 'Bachelor',
ectCount: 5,
faculty: 'testfaculty',
isWinter: true,
isSummer: false,
courseDescription: 'test.pdf',
enrollmentCourse: [ [Object] ]
}
]
Test result 2:
[
{
id: 2,
orderNumber: 1,
courseNumber: 'test123',
courseName: 'testcourse',
courseOfficer: 'testcontact',
degree: 'Bachelor',
ectCount: 5,
faculty: 'testfaculty',
isWinter: true,
isSummer: false,
courseDescription: 'test.pdf',
enrollmentCourse: [ [Object] ]
}
]
As we can see the first query does not work correctly. Can anybody give me a hint? Is there anything that I'm missing?
As per the doc you mentioned, you need to use some instead of every as you need at least one user returned if it matches.
const result1 = await this.prisma.course.findMany({
where: { enrollmentCourse: { some: { userId: user.id } } },
include: { enrollmentCourse: true }
});
This should give all the courses where the user is registered

Updating a many-to-many relationship in Prisma

I'm trying to figure out the right way to implement an upsert/update of the following schema:
model Post {
author String #Id
lastUpdated DateTime #default(now())
categories Category[]
}
model Category {
id Int #id
posts Post[]
}
Here is what I'd like to do. Get a post with category ids attached to it and insert it into the schema above.
The following command appears to insert the post
const post = await prisma.post.upsert({
where:{
author: 'TK'
},
update:{
lastUpdated: new Date()
},
create: {
author: 'TK'
}
})
My challenge is how do I also upsert the Category. I'll be getting a list of Catogories in the like 1,2,3 and if they do not exist I need to insert it into the category table and add the post to it. If the category does exist, I need to update the record with the post I inserted above preserving all attached posts.
Would appreciate it if I could be pointed in the right direction.
For the model, it can be simplified as follows as Prisma supports #updatedAt which will automatically update the column:
model Post {
author String #id
lastUpdated DateTime #updatedAt
categories Category[]
}
model Category {
id Int #id
posts Post[]
}
As for the query, it would look like this:
const categories = [
{ create: { id: 1 }, where: { id: 1 } },
{ create: { id: 2 }, where: { id: 2 } },
]
await db.post.upsert({
where: { author: 'author' },
create: {
author: 'author',
categories: {
connectOrCreate: categories,
},
},
update: {
categories: { connectOrCreate: categories },
},
})
connectOrCreate will create if not present and add the categories to the posts as well.

Sequelize – query include with belongsToMany or hasMany

I have User and Deal models. They are associated with belongsToMany relationship through UserDeals.
Let's say I have a deal like this:
const deal = {
id: 'my-id',
users: [{ id: 'user-a' }, { id: 'user-b' }]
};
How can I find it without having its id but having both users' ids?

sequelize-typescript many-to-many relationship model data with

I am using sequelize with sequelize-typescript library, and am trying to achieve the following relationship:
Team.ts
#Scopes({
withPlayers: {
include: [{model: () => User}]
}
})
#Table
export default class Team extends Model<Team> {
#AllowNull(false)
#Column
name: string;
#BelongsToMany(() => User, () => TeamPlayer)
players: User[];
}
User.ts
#Scopes({
withTeams: {
include: [{model: () => Team, include: [ () => User ]}]
}
})
#Table
export default class User extends Model<User> {
#AllowNull(false)
#Column
firstName: string;
#AllowNull(false)
#Column
lastName: string;
#BelongsToMany(() => Team, () => TeamPlayer)
teams: Team[];
}
TeamPlayer.ts
#DefaultScope({
include: [() => Team, () => User],
attributes: ['number']
})
#Table
export default class TeamPlayer extends Model<TeamPlayer> {
#ForeignKey(() => User)
#Column
userId: number;
#ForeignKey(() => Team)
#Column
teamId: number;
#Unique
#Column
number: number;
}
Now when querying for player, you get the object with the following data:
{
"id": 1,
"name": "Doe's Team",
"players": [
{
"id": 1,
"firstName": "John",
"lastName": "Doe",
"TeamPlayer": {
"userId": 1,
"teamId": 1,
"number": 32
}
}]
}
Now there are couple of things that I cannot get done..
1) I want to rename the TeamPlayer to something like "membership"; but not by changing the name of the class
2) the content of TeamPlayer should not have the id`s, but I want it to contain the data of the team, for example:
{
"firstName": "John",
"lastName": "Doe"
"membership": {
"number": 32
}
In the above classes, I tried to set a scope to the TeamPlayer class to only include number inside the TeamMember inclusion, but no effect.
I used to have the TeamPlayer class have direct memberships to team and player, but that solution added redundant id to the TeamPlayer class, and also did not prevent duplicate memberships in the team. I could indeed manually (= in code) prevent duplicates in these situations, but that does not feel elegant.
I ended up solving this by adding one-to-many relationships from TeamPlayer to User and Team, and also figured out the way to make the teamId + userId pair unique by adding two more fields with #ForeignKey like this:
TeamPlayer.ts
#Table
export default class TeamPlayer extends Model<TeamPlayer> {
#BelongsTo(() => Team)
team: Team;
#ForeignKey(() => Team)
#PrimaryKey
#Column
teamId: number;
#BelongsTo(() => User)
user: User;
#ForeignKey(() => User)
#PrimaryKey
#Column
userId: number;
#Column
number: number;
}
User.ts
#Table
export default class User extends Model<User> {
#AllowNull(false)
#Column
firstName: string;
#AllowNull(false)
#Column
lastName: string;
#HasMany(() => TeamPlayer)
teams: TeamPlayer[];
}
Team.ts
export default class Team extends Model<Team> {
#AllowNull(false)
#Column
name: string;
#HasMany(() => TeamPlayer)
players: TeamPlayer[];
}
This solution allows me to control the included query attributes via scopes, and gives me proper object output.