How to filter on relation in Prisma ORM - postgresql

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

Related

Prisma Client Select query on existence of value in joined table via Schema

In my instance i have a schema joining bonuses to a casino. query works great for data but I am unable to filter via the query itself. The where clause I used appears to be correct but I get an error the stating Object literal may only specify known properties, and 'nodeposit' does not exist in type. But I can query that data.
const data = await prisma.casino_p_casinos.findMany({
where: {
approved: 1,
rogue: 0,
bonuses: {
nodeposit: { gt : 0 },
}
},
select: {
id: true,
clean_name: true,
casino: true,
button: true,
bonuses: {
where: {
nodeposit: { gt: 0 },
},
},
},
take: 14,
});
If I remove the bonus pard in the WHERE clause the query works as expected but I want to grab all bonuses for each casino, but only if the bonuses contains a nodeposit value.
This nis what I want to use.
const data = await prisma.casino_p_casinos.findMany({
where: {
approved: 1,
rogue: 0,
bonuses: {
nodeposit: { gt : 0 },
},
},
select: {
id: true,
clean_name: true,
casino: true,
button: true,
bonuses: true,
},
take: 14,
});
SCHEMA :
model casino_p_casinos {
id Int #id #default(autoincrement())
casino String?
type String?
url String?
bonuses casino_p_bonus[]
model casino_p_bonus {
id Int #id #default(autoincrement())
parent Int
game String?
freespins Int?
freeplay String?
nodeposit Int?
deposit Int?
casino_p_casinos casino_p_casinos #relation(fields: [parent], references: [id])
}
You have a one to many relation, so when you add a where clause, you have one more layer with some, every or none like
const data = await prisma.casino_p_casinos.findMany({
where: {
approved: 1,
rogue: 0,
bonuses: {
// 'some' can be replaced by 'every' or 'none' here
some: {
nodeposit: { gt: 0 }
}
}
},
select: {
id: true,
clean_name: true,
casino: true,
button: true,
bonuses: true
},
take: 14
})
This query will filter casinos where some nodeposit are greater than 0 and return all bonuses, even those who are equals to 0.
And then, if you only want bonuses with nodeposit greater than 0 in casinos that have some, you should do:
const data = await prisma.casino_p_casinos.findMany({
where: {
approved: 1,
rogue: 0,
bonuses: {
// 'some' can be replaced by 'every' or 'none' here
some: {
nodeposit: { gt: 0 }
}
}
},
select: {
id: true,
clean_name: true,
casino: true,
button: true,
bonuses: {
where: {
nodeposit: { gt: 0 }
}
}
},
take: 14
})

Count or Include filtered relations prisma

I am currently stuck on a problem with my prisma queries.
I have an asset which has a 1 to Many relationship to views. I am trying to perform a findMany() on assets which returns either;
The asset with a list of views created within the last day
Or the asset with a count of views created in the last day
Finally I need to be able to orderBy this count or the count of views in my include statement. (this is what I am stuck on)
return await prisma.asset.findMany({
take: parseInt(pageSize),
skip: (pageSize * pageNumber),
include: {
_count: {
select: {
views: true
},
},
views: {
where: {
createdAt: dateFilter
},
},
likes: {
where: {
createdAt: dateFilter
}
},
transactions: true,
},
orderBy: { views: { _count: 'desc' } }
My queries does correctly return only views in my date range but how do I go about ordering the assets based on the count of these views. I have been stuck for quite some time on this. My raw SQL is not strong enough to write it from scratch at the moment.
If anyone has any ideas, thanks.
Will something like this work?
// First we group the views, with pagination
const groupedViews = await prisma.view.groupBy({
take: 10,
skip: 0,
by: ['postId'],
where: { createdAt: dateFilter },
_count: { postId: true },
orderBy: { _count: { postId: 'desc' } },
});
// Fetch the posts from the grouped views
const _posts = await prisma.post.findMany({
where: {
id: { in: groupedViews.map(({ postId }) => postId) },
},
include: {
_count: { select: { views: true } },
views: { where: { createdAt: dateFilter } },
},
});
// Map the fetched posts back for correct ordering
const posts = groupedViews.map(({ postId }) =>
_posts.find(({ id }) => id === postId)
);
Model:
model Post {
id String #id #default(cuid())
views View[]
}
model View {
id String #id #default(cuid())
createdAt DateTime #default(now())
postId String
post Post #relation(fields: [postId], references: [id])
}
This uses 2 separate queries, but does not require raw sql

Prisma : Models and Relationship 1-n

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' }
}

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.

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