Find rows where association count is between passed values - mysql

I am trying to find all the rows with their count of associated model.
It should only return those rows which have count of associations between two passed values.
This is what I tried, but getting sql exception.
db.Employee.findAll({
where: {
$assignedJobCount$: {
[Op.between]: [0, 100],
},
},
include: [
{
model: db.AssignedJob,
required: true,
attributes: ['emp_id'],
},
],
attributes: [
'id',
'alternate_mobile',
['employee_unique_id', 'unique_id'],
[Sequelize.fn('COUNT', Sequelize.col('AssignedJobs.emp_id')), 'assignedJobCount'],
],
order: [['id', 'ASC']],
subQuery: false,
group: ['Employee.id'],
limit: 20,
});
return JSON.parse(JSON.stringify(mechanics));
};
It throws below error,
sql:
'SELECT
`Employee`.`id`,
`Employee`.`alternate_mobile`,
`Employee`.`employee_unique_id` AS `unique_id`,
COUNT(`AssignedJobs`.`emp_id`) AS `assignedJobCount`,
`AssignedJobs`.`id` AS `AssignedJobs.id`,
`AssignedJobs`.`emp_id` AS `AssignedJobs.emp_id`
FROM
`employee` AS `Employee`
INNER JOIN `assigned_jobs` AS `AssignedJobs` ON
`Employee`.`id` = `AssignedJobs`.`emp_id`
WHERE
`assignedJobCount` BETWEEN 0 AND 100
GROUP BY
`Employee`.`id`
ORDER BY
`Employee`.`id` ASC
LIMIT 20;',
parameters: undefined
}
SequelizeDatabaseError: Unknown column 'assignedJobCount' in 'where clause'

I fixed it by converting where clause into having clause as below.
db.Employee.findAll({
having: {
assignedJobCount: {
[Op.between]: [0, 100],
},
},
include: [
{
model: db.AssignedJob,
required: true,
attributes: ['emp_id'],
},
],
attributes: [
'id',
'alternate_mobile',
['employee_unique_id', 'unique_id'],
[Sequelize.fn('COUNT', Sequelize.col('AssignedJobs.emp_id')), 'assignedJobCount'],
],
order: [['id', 'ASC']],
subQuery: false,
group: ['Employee.id'],
limit: 20,
});
return JSON.parse(JSON.stringify(mechanics));
};

Related

Sequelize order and limit data by 10 after getting all result

// returnJson: false,
model: Organization,
where: where,
limit: 10,
offset: 0,
order: [[{ model: Events }, "name", "desc"]],
include: [
{
model: Events,
through: { model: Organization_Event },
...softDeleteFilter,
},
],
});
**Organization can be linked to multiple events and vice versa. **
I want to order and limit data code is working fine. But when I apply for the order by asc or desc It orders the rows from the first 10 results.
I am expecting the following result:-
first to get all data and then apply the order after that return 10 result on database level
database: mysql

Sequelize join two columns of one table to the same column of another table

I have a table Relation with columns userId1 and userId2 it basically stores the relation between two users, userId1 and userId2 is foreign key here referenced from the User tables id (PK) column.
id: {
type: DataTypes.INTEGER.UNSIGNED,
autoIncrement: true,
primaryKey: true,
allowNull: false,
},
userId1: {
type: DataTypes.INTEGER.UNSIGNED,
allowNull: false,
},
userId2: {
type: DataTypes.INTEGER.UNSIGNED,
allowNull: false,
},
status: {
type: DataTypes.ENUM,
},
Then there is another table Posts containing information about posts.
id: {
type: DataTypes.INTEGER.UNSIGNED,
autoIncrement: true,
primaryKey: true,
allowNull: false,
},
content: {
type: DataTypes.TEXT,
allowNull: false,
},
postedBy: {
type: DataTypes.INTEGER.UNSIGNED,
allowNull: false,
},
I want to get lists of post of only those user who have a relation with me like friends, means my id lets suppose is 1 and it is in userId1 column and userId2 column has id in it, then I want to fetch all posts of value 2 from posts postedBy column.
This case can be vice versa as my id can be in userId2 column and I need to get all posts of the user whose value is in userId1 column.
I have read through all the questions and answers like multiple associations but it is just not working for me.
This is my associations in Posts model
Posts.hasOne(RelationModel, {
foreignKey: 'userId1',
sourceKey: 'postedBy',
as: 'first',
})
Posts.hasOne(RelationModel, {
foreignKey: 'userId2',
sourceKey: 'postedBy',
as: 'second',
})
Below is my include array.
include:[
{
model: RelationModel,
as: 'first',
where: {
status: 'accepted',
[Op.or]: [
{ userId1: request.user.id },
{ userId2: request.user.id },
],
},
},
{
model: RelationModel,
as: 'second',
where: {
status: 'accepted',
[Op.or]: [
{ userId1: request.user.id },
{ userId2: request.user.id },
],
},
}
]
The query being generated from this is below, where 151 is the logged in user id, means my id
SELECT
`posts`.*,
`first`.*,
`second`.*
FROM
`posts` AS `posts`
INNER JOIN
`relations` AS `first` ON `posts`.`postedBy` = `first`.`userId1`
AND (`first`.`userId1` = 151
OR `first`.`userId2` = 151)
AND `first`.`status` = 'accepted'
INNER JOIN
`relations` AS `second` ON `posts`.`postedBy` = `second`.`userId2`
AND (`second`.`userId1` = 151
OR `second`.`userId2` = 151)
AND `second`.`status` = 'accepted'
WHERE
`posts`.`deletedAt` IS NULL
ORDER BY `posts`.`id` ASC , `posts`.`id` ASC;
But the query I want to build is below
SELECT
`posts`.*,
`first`.*
FROM
`posts` AS `posts`
INNER JOIN
`relations` AS `first` ON (`posts`.`postedBy` = `first`.`userId2`
OR `posts`.`postedBy` = `first`.`userId1`)
AND (`first`.`userId1` = 151
OR `first`.`userId2` = 151)
AND `first`.`isFriend` = TRUE
AND `first`.`status` = 'accepted'
WHERE
`posts`.`deletedAt` IS NULL
ORDER BY `posts`.`id` ASC , `posts`.`id` ASC;
How to construct this query in sequelize?
You need to specify a unique as keyword for each relationship in the association as well as on the include for your query.
Posts.hasOne(RelationModel, {
foreignKey: 'userId1',
sourceKey: 'postedBy',
as: 'first',
});
Posts.hasOne(RelationModel, {
foreignKey: 'userId2',
sourceKey: 'postedBy',
as: 'second',
});
Then when you query you specify the unique as that identifies the relationship for the join
Post.findByPk(id, {
include: [{
model: RelationModel,
as: 'first',
},
{
model: RelationModel,
as: 'second',
}],
});

Sequelize: get MIN/MAX Dates from association

I have to tables - one for events and another one for the dates (start-end) this event is "active".
id name
-- ----
1 event 1
2 event 2
And then the EventDates
eventId start end
------- ----- ---
1 2018-01-14 2018-01-15
1 2018-01-17 2018-01-18
2 2018-02-14 2018-02-18
Events and EventDates are setup using hasMany()
Event.DATES = Event.hasMany(models.EventDates, {
as: 'dates',
foreignKey: 'eventId',
});
Querying for all Events including the EventDates like this:
await Event.findAll({
include: [{ association: Event.DATES }]
});
returns a nested Event list with all the EventDates - great.
[
{
id: 1,
name: 'event 1',
dates: [
{
start: '2018-01-14',
end: '2018-01-15',
},
{
start: '2018-01-17',
end: '2018-01-18',
},
],
},
{
id: 2,
name: 'event 2',
dates: [
{
start: '2018-02-14',
end: '2018-02-18',
},
],
}
]
But now I want to add a maxEndDate to an Event so I can check if it still active or if all EventDates are in the past. I can do that manually adding another LEFT JOIN like this:
# ...
LEFT OUTER JOIN (SELECT eventId, MAX(end)
FROM `EventDates`
GROUP BY eventId) dates
ON `Event`.`id` = `dates`.`eventId`
# ...
But how could I achieve the same thing with Sequelize?
[
{
id: 1,
name: 'event 1',
maxEndDate: ??,
dates: [
{
start: '2018-01-14',
end: '2018-01-15',
},
{
start: '2018-01-17',
end: '2018-01-18',
},
],
},
{
id: 2,
name: 'event 2',
maxEndDate: ??,
dates: [
{
start: '2018-02-14',
end: '2018-02-18',
},
],
}
]
It very tricky to get result with group by and has many association , but at last I found the way and here it is :
Event.findAll({
raw : true,
attributes : [ 'Event.*' , 'EventDates.eventId' , [sequelize.fn('max', sequelize.col('EventDates.end')),'end']],
include : {
model : EventDates ,
attributes :[]
},
group : ['Event.id','EventDates.eventId']
})
I think you can achieve your last out put that by this , I haven't tried this but this is the way you can go ahead
Event.findAll({
raw : true,
attributes : [ 'Event.*' , 'dates.*' , 'EventDates.eventId' , [sequelize.fn('max', sequelize.col('EventDates.end')),'end']],
include : [
{
model : EventDates ,
attributes :[]
},
{
model : EventDates ,
as : 'dates',
required : false,
separate : true
}
]
group : ['Event.id','EventDates.eventId']
})

Messages while migrating Ghost Blog from SQLite to MySQL

I tried to migrate ghost blog from SQLite to MySQL. I have completed the configuration as mentioned [here][1] I am getting following messages on command line. Is it correct or there is some issue in my config ?
$ npm start --production
> ghost#0.11.10 start /Ghost/Ghost
> node index
WARNING: Ghost is attempting to use a direct method to send email.
It is recommended that you explicitly configure an email service.
Help and documentation can be found at https://docs.ghost.org/v0.11.9/docs/mail-config.
[ { sql: 'show tables like ?',
output: [Function: output],
bindings: [ 'settings' ] } ]
{ method: 'first',
options: {},
timeout: false,
cancelOnTimeout: false,
bindings: [ 'databaseVersion', 1 ],
__knexQueryUid: '676a8f35-f680-4194-8214-0b9b54ba2kkk',
sql: 'select `value` from `settings` where `key` = ? limit ?' }
{ method: 'select',
options: {},
timeout: false,
cancelOnTimeout: false,
bindings: [],
__knexQueryUid: 'a882bfa0-8b4d-4f04-a638-0c971ee9kkkk',
sql: 'select `settings`.* from `settings`' }
[ { sql: 'show tables like ?',
output: [Function: output],
bindings: [ 'settings' ] } ]
{ method: 'first',
options: {},
timeout: false,
cancelOnTimeout: false,
bindings: [ 'databaseVersion', 1 ],
__knexQueryUid: '984e224e-9909-434e-b1d2-0c971ee9kkkk',
sql: 'select `value` from `settings` where `key` = ? limit ?' }
{ method: 'select',
options: {},
timeout: false,
cancelOnTimeout: false,
bindings: [],
__knexQueryUid: 'f6b8795b-7af2-4b39-a18d-0c971ee9kkkk',
sql: 'select `permissions`.* from `permissions`' }
{ method: 'select',
options: {},
timeout: false,
cancelOnTimeout: false,
bindings: [ 'activeTheme', 1 ],
__knexQueryUid: 'd5c924a7-4020-4cff-8a8d-0c971ee9kkkk',
sql: 'select `settings`.* from `settings` where `settings`.`key` = ? limit ?' }
{ method: 'select',
options: {},
timeout: false,
cancelOnTimeout: false,
bindings: [],
__knexQueryUid: '6f7eb24c-e3c7-4197-a308-0c971ee9kkkk',
sql: 'select `settings`.* from `settings`' }
{ method: 'select',
options: {},
timeout: false,
cancelOnTimeout: false,
bindings: [ 'installedApps', 1 ],
__knexQueryUid: '0bf06724-21e3-46b0-9b9e-0c971ee9kkkk',
sql: 'select `settings`.* from `settings` where `settings`.`key` = ? limit ?' }
{ method: 'update',
options: {},
timeout: false,
cancelOnTimeout: false,
bindings:
[ '2017-07-03 12:39:55',
1,
25,
'installedApps',
'app',
'2017-07-03 12:51:14',
1,
'f45a2c39-8be1-4858-a213-0c971ee9kkkk',
'[]',
25 ],
__knexQueryUid: '018b0cc4-f1c5-4e95-9dd0-0c971ee9kkkk',
sql: 'update `settings` set `created_at` = ?, `created_by` = ?, `id` = ?, `key` = ?, `type` = ?, `updated_at` = ?, `updated_by` = ?, `uuid` = ?, `value` = ? where `id` = ?' }
{ method: 'select',
options: {},
timeout: false,
cancelOnTimeout: false,
bindings: [ 1499086274940 ],
__knexQueryUid: 'aa394d2a-e425-4652-ab83-0c971ee9kkkk',
sql: 'select `accesstokens`.* from `accesstokens` where `expires` < ?' }
{ method: 'select',
options: {},
timeout: false,
cancelOnTimeout: false,
bindings: [ 1499086274941 ],
__knexQueryUid: '1a374b81-1c98-4d01-81d0-0c971ee9kkkk',
sql: 'select `refreshtokens`.* from `refreshtokens` where `expires` < ?' }
{ method: 'select',
options: {},
timeout: false,
cancelOnTimeout: false,
bindings: [ 'ghost-scheduler', 1 ],
__knexQueryUid: '0a851e78-5a7e-422a-9b5a-0c971ee9kkkk',
sql: 'select `slug`, `secret` from `clients` where `clients`.`slug` = ? limit ?' }
{ method: 'select',
options: {},
timeout: false,
cancelOnTimeout: false,
bindings: [ 'scheduled' ],
__knexQueryUid: '0663f666-fe0f-4115-91ce-0c971ee9kkkk',
sql: 'select `id`, `published_at`, `created_at` from `posts` where `posts`.`status` = ?' }
Ghost is running in production...
Your blog is now available on http://localhost:2368
Ctrl+C to shut down
{ method: 'select',
options: {},
timeout: false,
cancelOnTimeout: false,
bindings: [],
__knexQueryUid: '287f9644-4baa-476a-84a7-0c971ee9kkkk',
sql: 'select `settings`.* from `settings`' }
[1]: http://www.techtrekking.net/how-to-migrate-ghost-blog-from-sqlite3-to-mysql/
Please note that blog us working fine but I am worried about these messages 1). if I have done any wrong config or 2). These extra display might impact server performance ?
Do I need to make any changes other than what is mentioned at http://www.techtrekking.net/how-to-migrate-ghost-blog-from-sqlite3-to-mysql/

Excluding foreign key attributes causes nested queries to fail

On all my models I have a created by and updated by foreign key columns that link to my user model. When I query my database using findAll() for example, I always include them so that the user's username and other details are returned with my results - I also exclude the foreign key columns for the result at it makes no sense returning the created by object which includes the ID and and the createdById node as it's just duplicate data
"createdById": "21499bb6-4476-11e6-beb8-9e71128cae77",
"createdBy": {
"id": "21499bb6-4476-11e6-beb8-9e71128cae77",
"username": "mo.gusbi"
},
"updatedById": "21499bb6-4476-11e6-beb8-9e71128cae77",
"updatedBy": {
"id": "21499bb6-4476-11e6-beb8-9e71128cae77",
"username": "mo.gusbi"
}
So what I do is just exclude createdById and updatedById attributes and this works great, until I have to join other tables and include constraints (for pagination, that cause a subquery to be generated) which looks for the createdById and updatedById columns that I originally excluded, causing an SQL error
SequelizeDatabaseError: SQLITE_ERROR: no such column: Category.createdById
from the generated query
SELECT `Category`.*, `children`.`id` AS `children.id`,
`children`.`name` AS `children.name`,
`createdBy`.`id` AS `createdBy.id`,
`createdBy`.`username` AS `createdBy.username`,
`updatedBy`.`id` AS `updatedBy.id`,
`updatedBy`.`username` AS `updatedBy.username`,
`deletedBy`.`id` AS `deletedBy.id`,
`deletedBy`.`username` AS `deletedBy.username`
FROM (
SELECT `Category`.`id`,
`Category`.`name`,
`Category`.`parentId`
FROM `Categories` AS `Category`
WHERE (`Category`.`deletedAt` IS NULL
AND `Category`.`parentId` = NULL)
LIMIT 0, 10
) AS `Category`
LEFT OUTER JOIN `Categories` AS `children`
ON `Category`.`id` = `children`.`parentId`
AND `children`.`deletedAt` IS NULL
LEFT OUTER JOIN `Users` AS `createdBy`
ON `Category`.`createdById` = `createdBy`.`id`
AND `createdBy`.`deletedAt` IS NULL
LEFT OUTER JOIN `Users` AS `updatedBy`
ON `Category`.`updatedById` = `updatedBy`.`id`
AND `updatedBy`.`deletedAt` IS NULL
LEFT OUTER JOIN `Users` AS `deletedBy`
ON `Category`.`deletedById` = `deletedBy`.`id`
AND `deletedBy`.`deletedAt` IS NULL
;
Models
User = sequelize.define('User', {
id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV1,
primaryKey: true
},
username: {
type: DataTypes.STRING,
unique: true,
allowNull: false,
validate: {
notEmpty: true,
is: regex.username
}
}
}, {
paranoid: true,
classMethods: {
associate: (models) => {
User.belongsTo(models.User, {
foreignKey: 'createdById',
as: 'createdBy'
});
User.belongsTo(models.User, {
foreignKey: 'updatedById',
as: 'updatedBy'
});
User.belongsTo(models.User, {
foreignKey: 'deletedById',
as: 'deletedBy'
});
}
}
});
Category = sequelize.define('Category', {
id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV1,
primaryKey: true
},
name: {
type: DataTypes.STRING,
allowNull: false,
validate: {
notEmpty: true
}
}
}, {
indexes: [{
unique: true,
fields: ['name', 'parentId']
}],
paranoid: true,
classMethods: {
associate: (models: any) => {
Category.belongsTo(models.User, {
foreignKey: 'createdById',
as: 'createdBy'
});
Category.belongsTo(models.User, {
foreignKey: 'updatedById',
as: 'updatedBy'
});
Category.belongsTo(models.User, {
foreignKey: 'deletedById',
as: 'deletedBy'
});
Category.belongsTo(models.Category, {
foreignKey: 'parentId',
as: 'parent'
});
Category.hasMany(models.Category, {
foreignKey: 'parentId',
as: 'children'
});
}
}
});
Query
Category
.findAll({
attributes: [
'id',
'name',
'parentId'
],
where: {
parentId: req.query.parentId
},
include: [
{
model: Category,
as: 'children',
attributes: [
'id',
'name'
]
},
{
model: User,
as: 'createdBy',
attributes: [
'id',
'username'
]
},
{
model: User,
as: 'updatedBy',
attributes: [
'id',
'username'
]
},
{
model: User,
as: 'deletedBy',
attributes: [
'id',
'username'
]
}
],
offset: 0,
limit: 10
});