Multiple Model Count Sequelize - postgresql

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

Related

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

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 }

Sequelize Get counts of associated model

I have Roles Model, Member Model as a parent model, and MemberRole as a child
Role.belongsToMany(models.Member, {foreignKey: 'roleId', through: models.MemberRole});
Role.hasMany(models.MemberRole, {foreignKey: 'roleId', as: 'roleMembers'});
Currently, it is giving count irrespective of Member Status. I want to get Count of memberRoles if Member has Active status
subQuery: false,
where: {accountId: accountId},
attributes: {
include: [
[Sequelize.literal('COUNT(DISTINCT("roleMembers"."id"))'), 'assignedMemberCount'],
[Sequelize.literal('COUNT(DISTINCT("roleGroups"."id"))'), 'assignedGroupCount'],
]
},
include: [{
model: MemberRole,
as: 'roleMembers',
attributes: [],
required: false
},
order: [['name', 'ASC']],
group: ['Role.id'],
limit: options.limitCount,
offset: options.skipCount
The most correct way would be to use subqueries especially when you use limit and offset for the main model:
subQuery: false,
where: {accountId: accountId},
attributes: {
include: [
[Sequelize.literal('SELECT COUNT("roleMembers"."id") FROM roleMembers where roleMembers.roleId=roles.id'), 'assignedMemberCount'],
]
},
order: [['name', 'ASC']],
limit: options.limitCount,
offset: options.skipCount
I didn't indicate assignedGroupCount because I didn't see include for roleGroups. You can add it using the assignedMemberCount subquery as an example.

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

How to remove hasMany id attribute from sequelize join?

I am attempting to join two tables defined below in a one-to-many relationship. One Characteristic to many ReviewCharacteristics. Using the following findAll function I am attempting to avg the values in the ReviewCharacteristics table. I have verified the necessary SQL command that should function as expected. Sequelize is including ReviewCharateristic.id in the selected attributes which is causing my grouping to fail.
Is there a way to specify just the avg value and not include the id from the ReviewCharacteristic table in the selected attributes?
exports.Characteristic = db.define('characteristic', {
product_id: {
type: DataTypes.INTEGER,
allowNull: false
},
name: {
type: DataTypes.STRING(7),
allowNull: false
}
}, { underscored: true });
exports.ReviewCharacteristic = db.define('review_characteristic', {
value: {
type: DataTypes.INTEGER,
allowNull: false
}
}, { underscored: true });
exports.Characteristic.hasMany(exports.ReviewCharacteristic, { as: 'rc' });
exports.ReviewCharacteristic.belongsTo(exports.Characteristic);
Characteristic.findAll({
attributes: ['name', 'id'],
where: {
product_id: req.query.product_id
},
group: 'characteristic.id',
include: [{
model: ReviewCharacteristic,
as: 'rc',
attributes: [[Sequelize.fn('AVG', Sequelize.col('rc.value')), 'value']],
required: true
}]
});
I have also tried specifying attributes: ['characteristic.name', 'characteristic.id']
Current:
SELECT
"characteristic"."name",
"characteristic"."id",
"rc"."id" AS "rc.id",
AVG("rc"."value") AS "rc.value"
FROM "characteristic" AS "characteristic"
INNER JOIN "review_characteristic" AS "rc"
ON "characteristic"."id" = "rc"."characteristic_id"
WHERE "characteristic"."product_id" = '18078'
GROUP BY "characteristic"."id";
Expected:
SELECT
"characteristic"."name",
"characteristic"."id",
AVG("rc"."value") AS "rc.value"
FROM "characteristic" AS "characteristic"
INNER JOIN "review_characteristic" AS "rc"
ON "characteristic"."id" = "rc"."characteristic_id"
WHERE "characteristic"."product_id" = '18078'
GROUP BY "characteristic"."id";
If anyone happens to stumble across this question and is dealing with the same issue here is the solution I found.
Characteristic.findAll({
attributes: ['name', 'id', [Sequelize.fn('AVG', Sequelize.col('rc.value')), 'value']],
where: {
product_id: req.query.product_id
},
group: 'characteristic.id',
include: [{
model: ReviewCharacteristic,
as: 'rc',
attributes: [],
required: true
}]
});
The attributes must be specified as an empty array for the joined table in order to stop the addition of the ReviewCharateristic.id attribute in my case.

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