Prisma : Models and Relationship 1-n - prisma

I have two tables User and Tasks and a user can have many tasks, however i want a query to return a particular task, fetching details for the task, with author and assigned to users from the user table, usually would be done using aliases. DB is mysql - Thanks
//schema.prisma
model User {
id Int #id #default(autoincrement())
taskby Task[] #relation("taskBy")
taskto Task[] #relation("taskTo")
}
model Task {
id Int #id #default(autoincrement())
created_at DateTime #default(now())
updated_at DateTime #updatedAt
assigned_to_uid Int
assigned_by_uid Int
assigned_to User #relation("taskTo",fields: [assigned_to_uid], references: [id])
assigned_by User #relation("taskBy",fields: [assigned_by_uid], references: [id])
}
API:
if (id) {
res = await prisma.task.findUnique({
where: { id },
include: {
assigned_to: true
},
include: {
assigned_by: true
},
})
} else {...
Desired Response:
{
"id": 2,
"taskid": 2,
"assigned_to_uid": 1,
"assigned_by_uid": 2,
"assigned_by": {
"id": 2,
"firstName": "user2",
},
"assigned_to": {
"id": 1
"firstName": "user1",
},
}

You should be able to get the desired response by using the below query:
if (id) {
const response = await prisma.task.findUnique({
where: { id },
include: {
assigned_to: true,
assigned_by: true,
},
});
console.log(response);
}
Response for the above query:
{
id: 1,
created_at: 2022-02-28T07:22:06.917Z,
updated_at: 2022-02-28T07:22:06.918Z,
assigned_to_uid: 2,
assigned_by_uid: 1,
assigned_to: { id: 2, firstName: 'Jane', lastName: 'Doe' },
assigned_by: { id: 1, firstName: 'John', lastName: 'Doe' }
}

Related

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 can I create a nested item in the DB?

I am having several problems when I try to create a new user with a nested related table.
My schema is like this:
model User {
id Int #id #default(autoincrement())
username String #unique
email String #unique
avatar String #default("none")
role Role #default(USER)
password String
maxScore Int #default(0)
UserCategoryStats UserCategoryStats[]
}
model UserCategoryStats {
id Int #id #default(autoincrement())
user User #relation(fields: [userId], references: [id])
userId Int
category Category #relation(fields: [categoryId], references: [id])
categoryId Int
correctAnswers Int #default(0)
incorrectAnswers Int #default(0)
totalAnswers Int #default(0)
timesChosen Int #default(0)
}
And I am just trying to create an user with one(or more) UserCategoryStats like the docs say
Like this:
const newUser = await prisma.user.create({
data: {
username,
email,
password: hashedPassword,
UserCategoryStats: {
create: { //Here is the error
correctAnswers: 0,
incorrectAnswers: 0,
totalAnswers: 0,
timesChosen: 0,
},
},
},
include: {
UserCategoryStats: true,
},
});
This is giving me this Typescript error :
Type '{ correctAnswers: number; incorrectAnswers: number; totalAnswers: number; timesChosen: number; }' is not assignable to type '(Without<UserCategoryStatsCreateWithoutUserInput, UserCategoryStatsUncheckedCreateWithoutUserInput> & UserCategoryStatsUncheckedCreateWithoutUserInput) | ... 5 more ... | undefined'.
Type '{ correctAnswers: number; incorrectAnswers: number; totalAnswers: number; timesChosen: number; }' is not assignable to type 'undefined'
I have tried with multiple values but I simply dont know what is wrong. I am not able to create nested properties.
What am I doing wrong?
Considering the Prisma Query and Schema that you have shared there were no typescript errors, it can be that your PrismaClient is not in sync with the schema, you can execute npx prisma generate to verify if both are in sync.
Here's a working example:
main.ts
import { PrismaClient } from '#prisma/client';
const prisma = new PrismaClient({
log: ['query'],
});
async function main() {
const newUser = await prisma.user.create({
data: {
username: 'alice',
email: 'alice#prisma.io',
password: 'Prisma#111',
UserCategoryStats: {
create: {
//Here is the error
correctAnswers: 0,
incorrectAnswers: 0,
totalAnswers: 0,
timesChosen: 0,
},
},
},
include: {
UserCategoryStats: true,
},
});
console.log(newUser);
}
main()
.catch((e) => {
throw e;
})
.finally(async () => {
await prisma.$disconnect();
});
Response:
> ts-node index.ts
prisma:query BEGIN
prisma:query INSERT INTO "public"."User" ("username","email","avatar","password","maxScore") VALUES ($1,$2,$3,$4,$5) RETURNING "public"."User"."id"
prisma:query INSERT INTO "public"."UserCategoryStats" ("userId","correctAnswers","incorrectAnswers","totalAnswers","timesChosen") VALUES ($1,$2,$3,$4,$5) RETURNING "public"."UserCategoryStats"."id"
prisma:query SELECT "public"."User"."id", "public"."User"."username", "public"."User"."email", "public"."User"."avatar", "public"."User"."password", "public"."User"."maxScore" FROM "public"."User" WHERE "public"."User"."id" = $1 LIMIT $2 OFFSET $3
prisma:query SELECT "public"."UserCategoryStats"."id", "public"."UserCategoryStats"."userId", "public"."UserCategoryStats"."correctAnswers", "public"."UserCategoryStats"."incorrectAnswers", "public"."UserCategoryStats"."totalAnswers", "public"."UserCategoryStats"."timesChosen" FROM "public"."UserCategoryStats" WHERE "public"."UserCategoryStats"."userId" IN ($1) OFFSET $2
prisma:query COMMIT
{
id: 1,
username: 'alice',
email: 'alice#prisma.io',
avatar: 'none',
password: 'Prisma#111',
maxScore: 0,
UserCategoryStats: [
{
id: 1,
userId: 1,
correctAnswers: 0,
incorrectAnswers: 0,
totalAnswers: 0,
timesChosen: 0
}
]
}
Inserted Data:

Unable to filter an array inside of a related model with Prisma 2

I'm trying to check if the provided value exists inside of an array. I've been trying to figure this one out and from what I gathered, I have to use has. The array I'm trying to filter is inside of a related model. I tried looking for a solution, but couldn't find much on this subject. Am I doing something wrong? Is it at all possible to filter an array inside of a related model?
Here's my schema. Job and Company models are related, and inside Company we have a parking array.
model Company {
id Int #id #default(autoincrement())
name String #db.VarChar(200)
state String #db.VarChar(30)
parking String[]
...
createdAt DateTime #default(now())
updated_at DateTime #updatedAt
##map(name: "company")
}
model Job {
id Int #id #default(autoincrement())
type String
company Company #relation(fields: [company_id], references: [id])
company_id Int
createdAt DateTime #default(now())
updated_at DateTime #updatedAt
UserJobs UserJobs[]
##map(name: "job")
}
Below, I'm trying to find many jobs which match various values. One of the values I'm trying to match is inside of an array in the related Company model. Here's what I tried:
const jobs = await prisma.job.findMany({
where: {
AND: [
{
type: {
contains: req.body.type,
}
},
{
company: {
state: {
contains: req.body.state
}
}
},
...
{
company: {
parking: {
has: req.body.parkingState
}
}
}
]
},
include: {
company: true,
}
})
If you want to match a single value in a list has should be used, but if you want to match multiple values in a list then you would need to use hasEvery or hasSome depending upon your use case.
Here is the query which matches a single value in a list
const jobs = await prisma.job.findMany({
where: {
AND: [
{
type: {
contains: 'Software Engineer',
},
},
{
company: {
state: {
contains: 'CA',
},
},
},
{
company: {
parking: {
has: 'Employee',
},
},
},
],
},
include: {
company: true,
},
});
console.log(JSON.stringify(jobs, null, 2));
}
Here is the response for the above query:
[
{
"id": 1,
"type": "Software Engineer",
"company_id": 1,
"createdAt": "2022-02-28T08:53:03.949Z",
"updated_at": "2022-02-28T08:53:03.950Z",
"company": {
"id": 1,
"name": "Apple",
"state": "CA",
"parking": [
"Admin",
"Manager",
"Employee"
],
"createdAt": "2022-02-28T08:50:50.030Z",
"updated_at": "2022-02-28T08:50:50.031Z"
}
}
]
This is the sample data with which the above query fetched the results.
Job Table:
Company Table:
If you want to match multiple values in parking array you could achieve it by replacing has with hasSome in this manner.
const jobs = await prisma.job.findMany({
where: {
AND: [
{
type: {
contains: 'Software Engineer',
},
},
{
company: {
state: {
contains: 'CA',
},
},
},
{
company: {
parking: {
hasSome: ['Employee', 'Manager'],
},
},
},
],
},
include: {
company: true,
},
});
console.log(JSON.stringify(jobs, null, 2));
}

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) },
},
},
},
},
});

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