Count or Include filtered relations prisma - 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

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

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.

How to get All items associated to a user in mongoose

I have a collection of Items that a particular make and perform a transaction. In my schema, I associate the userId to each item. I want to be able to display as a list all the items that the user owns.
Here I have managed to total up all sizes of each item but I cant work out a way how to get a total for each user
{
id: Number,
x: Number,
y: Number,
xSize: String,
ySize: String,
imageSource: String,
user: { type: mongoose.Schema.ObjectId, ref: 'User' }
},
{ timestamps: true }
);
const UserSchema = new Schema(
{
id: Number,
name: String,
website: String,
},
{ timestamps: true }
);
Item.find({}, function (err, items) {
var itemMap = {};
items.forEach(function (item) {
itemMap[item._id] = item;
});
var countedNames = items.reduce(function (allNames, name) {
if (name.xSize in allNames) {
allNames[name.xSize]++;
}
else {
allNames[name.xSize] = 1;
}
return allNames;
}, {});
Essentially i want to get a list basically saying
{name:"Dave", website:"www.google.com, items:[item1, item2]}
where item1 and item2 relate to the item schema
You should rewrite your UserSchema to contain a reference to the item, in this format:
const UserSchema = new Schema(
{
id: Number,
name: String,
website: String,
items:
[{
item: {type: mongoose.Schema.ObjectId, ref: 'Item'}
}]
},
{ timestamps: true }
);
This will simply allow you to perform the following query:
User.find({}).populate('Item')
Which would return the User document, and all items associated under the document.
You could do the following:
let users = User
.find({})
.populate('Item')
.exec(function (err, users) {
if (err) { console.log(err); }
console.log(users)
}
Rewriting the schema will make querying users for their items much easier.

Get all the data of a field but populate only 10 of them in mongodb

I have User model with this friends schema:
friends: [{
type : Schema.Types.ObjectId,
ref: 'User'
}],
I tried this:
const user = await User.findById({ _id: userID })
.populate({ path: 'friends', options: { limit: 10 } })
this works.... but it actually loads and populate only 10 of the friends. I need to load all of them to display the count of the friends and populate 10 to display user avatar and such things...
How can I do this?
Also I have simimar problem with this schema:
comments: [{
user: {
type : Schema.Types.ObjectId,
ref: 'User'
},
comment: {
type: String
},
date: {
type: Date,
default: Date.now
}
}]
I tried this:
It populate all of the comments.user but how should I do this here because this:
const user = await User.findById({ _id: userID })
.populate({ path: 'comments.user', options: { limit: 10 } })
doesn't limit them....
You might be able to fix that by using aggregation + populate kind of like this (untested):
var result = User.aggregate([{
$match: { // filter by user id
_id: userID
}
}, {
$addFields: { // add count of friends
numberOfFriends: { $size: "$friends" }
}
}]);
and then
User.populate(result, { path: "friends", options: { limit: 10 } }, /* your callback */);