How to get average from related table for findAll in Sequelize.js? - postgresql

I can get the average of the related table for a specific entity, but I don't know how to do it for all entities
I want to get an average rating for one movie
this.movieRepository.findByPk(id, {
include: {
model: Rating,
as: 'ratings',
attributes: [],
},
attributes: {
include: [
[
Sequelize.fn('AVG', Sequelize.col('ratings.score')), 'rating',
],
],
},
group: ['Movie.id'],
});
And I get it
{
id: 1337,
...,
rating: 3,1234567,
}
How to do the same for all elements? (in this.movieRepository.findAll method)
Rating is just entity with { movieId, userId, score }

Related

Multiple Model Count Sequelize

I have a Model associated with two more models by hasMany.
Need a count of both Model's ids
Model.findAndCountAll({
attributes: {
include: [
[sequelize.fn('COUNT', sequelize.col('IncludeModel1.id')), 'includeModel1Count'],
[sequelize.fn('COUNT', sequelize.col('IncludeModel2.id')), 'IncludeModel2Count']
]
},
include: [
{ model: IncludeModel2, attributes: [] },
{
model: IncludeModel1,
attributes: [],
required: true
}
],
subQuery: false,
limit: this.details.limit || 10,
offset: this.details.offset || 0,
group: ['Model.id']
})
Gives Wrong Count
Can you show me the right way?
Thank You

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

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

Sequelize - How return rows only if included model has at least one record

I want to this query in Sequelize:
const results = await WorkflowAssignation.findAndCountAll(
{
limit: limit,
offset: offset,
where:
{
userId: userIds,
status: status
},
include: [
{
model: WorkflowStepAssignation,
include: [
{ model: WorkflowStepAssignationHasUser, include: [ users ]}
]
},
{ model: users }
],
distinct: true,
order: [ [ 'createdAt', 'DESC'], [ { model: WorkflowStepAssignation, as: "steps" }, 'stepNumber', 'ASC' ] ]
});
The problem is that I need to return only WorkflowAssignation if the WorkflowStepAssignationHasUser table contain at least 1 record with the value passed as parameter for userid.
if I used a where in this line { model: WorkflowStepAssignationHasUser, include: [ users ]} it will still return all the WorkflowAssignation with only the steps that has the userId value and that's not what I want.
It is like I want to return all the WorkflowAssignation including all the tables but only if in WorkflowStepAssignation -> WorkflowStepAssignationHasUser has the users in the parameters.
Relations are:
WorkflowAssignation
has Many
WorkflowStepAssignation
has Many
WorkflowStepAssignationHasUser (has a userId column)
add required: true
const results = await WorkflowAssignation.findAndCountAll(
{
limit: limit,
offset: offset,
where:
{
userId: userIds,
status: status
},
include: [
{
model: WorkflowStepAssignation,
include: [
{ model: WorkflowStepAssignationHasUser, include: [ users ]}
]
},
{ model: users }
],
distinct: true,
required: true,
order: [ [ 'createdAt', 'DESC'], [ { model:
WorkflowStepAssignation, as: "steps" }, 'stepNumber', 'ASC' ] ]
});

Sequelize: Returning a single COUNT entry instead of every row in a nested include

I'm trying to grab the total number of rows (count) from a nested include in Sequelize. I've followed countless questions on here, but can't seem to get it right.
Models:
Brand
BrandProfile (alias: brand_profile)
BrandPageView (alias brand_page_view)
Relationships:
// Brand
Brand.hasOne(models.BrandProfile, {
foreignKey: 'brand_id',
as: 'brand_profile'
})
// BrandProfile
BrandProfile.belongsTo(models.Brand, {
foreignKey: 'brand_id',
})
BrandProfile.hasMany(models.BrandPageView, {
foreignKey: 'brand_id',
as: 'brand_page_views'
})
// BrandPageView
BrandProfile.belongsTo(models.BrandProfile, {
foreignKey: 'brand_id',
})
Now when I try to normally run my query like so:
const { count, rows } = await Brand.findAndCountAll({
include: [
{
model: BrandProfile,
as: 'brand_profile',
include: [
{
model: BrandPageView,
as: 'brand_page_views',
},
],
},
],
})
It returns the following:
{
id: 1,
created_at: '2020-12-26T20:42:19.930Z',
updated_at: '2020-12-29T20:46:58.918Z',
deleted_at: null,
name: 'Test brand',
slug: 'test-brand',
status: 'disabled',
brand_profile: {
created_at: '2020-12-26T20:42:19.931Z',
about: [ [Object], [Object], [Object], [Object] ],
cleaned_about: null,
subtitle: 'subtitle test',
photo: '1609477287152.jpeg',
photo_config: { scale: '1.00' },
id: 1,
updated_at: '2021-01-01T05:01:27.414Z',
brand_id: 1,
brand_page_views: [
[Object], [Object],
[Object], [Object],
[Object], [Object],
[Object]
]
},
}
As you can see, brand.brand_profile.brand_page_views has a list of all objects. Now I just want to return the count, so I'm using the following query:
const { count, rows } = await Brand.findAndCountAll({
include: [
{
model: BrandProfile,
as: 'brand_profile',
include: {
model: BrandPageView,
as: 'brand_page_views',
attributes: [],
},
},
],
attributes: {
include: [
[
Sequelize.fn('COUNT', Sequelize.col('brand_profile.brand_page_views.brand_id')),
'brand_page_views_count',
],
],
},
group: ['brand.id']
})
I get this error:
'missing FROM-clause entry for table "brand"'
The SQL it outputs is:
SELECT "Brand"."id", "Brand"."created_at", "Brand"."updated_at", "Brand"."deleted_at", "Brand"."name", "Brand"."slug", "Brand"."status", COUNT("brand_profile->brand_page_views"."brand_id") AS "brand_profile.brand_page_views.count", "brand_profile"."created_at" AS "brand_profile.created_at", "brand_profile"."about" AS "brand_profile.about", "brand_profile"."cleaned_about" AS "brand_profile.cleaned_about", "brand_profile"."subtitle" AS "brand_profile.subtitle", "brand_profile"."photo" AS "brand_profile.photo", "brand_profile"."email" AS "brand_profile.email", "brand_profile"."photo_config" AS "brand_profile.photo_config", "brand_profile"."id" AS "brand_profile.id", "brand_profile"."updated_at" AS "brand_profile.updated_at", "brand_profile"."brand_id" AS "brand_profile.brand_id" FROM "brands" AS "Brand" LEFT OUTER JOIN "brand_profile" AS "brand_profile" ON "Br2021-01-05 01:32:26 [sequelize] INFO Executing (default): SELECT "Brand"."id", "Brand"."created_at", "Brand"."updated_at", "Brand"."deleted_at", "Brand"."name", "Brand"."slug", "Brand"."status", COUNT("brand_profile->brand_page_views"."brand_id") AS "brand_profile.brand_page_views.count", "brand_profile"."created_at" AS "brand_profile.created_at", "brand_profile"."about" AS "brand_profile.about", "brand_profile"."cleaned_about" AS "brand_profile.cleaned_about", "brand_profile"."subtitle" AS "brand_profile.subtitle", "brand_profile"."photo" AS "brand_profile.photo", "brand_profile"."email" AS "brand_profile.email", "brand_profile"."photo_config" AS "brand_profile.photo_config", "brand_profile"."id" AS "brand_profile.id", "brand_profile"."updated_at" AS "brand_profile.updated_at", "brand_profile"."brand_id" AS "brand_profile.brand_id" FROM "brands" AS "Brand" LEFT OUTER JOIN "brand_profile" AS "brand_profile" ON "Brand"."id" = "brand_profile"."brand_id" LEFT OUTER JOIN "brand_page_views" AS "brand_profile->braand"."id" = "brand_profile"."brand_id" LEFT OUTER JOIN "brand_page_views" AS "brand_profile->brand_page_views" ON "brand_profile"."id" = "brand_profile->brand_page_views"."brand_id" WHERE "Brand"."id" = 1 GROUP BY "brand"."id";
nd_page_views" ON "brand_profile"."id" = "brand_profile->brand_page_views"."brand_id" WHERE "Brand"."id" = 1 GROUP BY "brand"."id";
I've honestly tried this so many ways, added brand_profile->brand_page_views.brand_id, brand_profile.brand_id to the group, but to no avail.
Sequelize was not designed to support a wide variety of SQL aggregations. So in your case you should use sequelize.literal with a subquery to count child records:
attributes: {
include: [
[Sequelize.literal('(select COUNT(*) from brand_page_views where brand_page_views.brand_id=brand_profile.id)'),
'brand_page_views_count'],
],
},
Don't forget to remove group: ['brand.id']