Sequelize: How to multiply column into an aggregate function - mysql

So basically what I want is:
select (table2.col1 * sum(table1.col1)) as myAggregate
from table1
join table2 ON table2.id = table1.id
I've gotten this far but don't know how to add the multiplication:
Table1Model.findAll({
attributes: [[Sequelize.fn('SUM', 'col1'), 'myAggregate']]
include: [Table2Model]
})

I ended up going for 'raw' mysql. Note that this won't be cross-database compatible. Here's what I did in code (this is mySQL)
attributes: [
[Sequelize.literal('(`Meetup.MeetupBusinessPercentages`.percentageCut * SUM(MeetupCharges.amount) / 100 )'), 'totalAmount']
],

I don't know if this is covered by Sequelize docs, but you could use Sequelize.where to accomplish this. You might need to change table names, but it would be roughly like this:
Table1Model.findAll({
attributes: [
[
Sequelize.where(
Sequelize.col('table2.col1'),
'*',
Sequelize.fn('SUM', Sequelize.col('col1')),
),
'myAggregate',
]
],
include: [Table2Model]
})

Related

how to get rows that has at least 1 association row with sequelize

I have 'Ingredient' and 'Log' Tables like this
[Ingredient Table]
id
..
...
[Log Table]
id
Ingredient_id
record_date
..
...
the relationship is Log.belongsTo(Ingredeint)
how can I find all ingredients which have at least 1 row of Log?
I mean when I searching the Ingredients, If there is no related Log on Ingredient, I don't want to include that Ingredient on my search result.
what I did now to accomplish that is
const ingredients = await Ingredient.findAll({
include: {
model: Log
},
group: "id",
attributes: {
include: [
[sequelize.fn("COUNT", sequelize.col("record_date")), "order_count"]
]
}
})
const sortedIngredient = ingredients
.filter(ingredient => ingredient.dataValues.order_count > 0)
But I think there would be a better way.
Thank you for reading this.
If I understand you correctly you want to do a inner join in your include, then you would only return ingredients that have some match in the included model.
Try to change the include to:
include: {
model: Log
required: true, // <-- Add this row
}
More info about require can be found in the docs: https://sequelize.org/master/class/lib/model.js~Model.html#static-method-findAll
Another option that maybe could help you is to add having to filter on a aggregated column, like this:
const ingredients = await Ingredient.findAll({
include: {
model: Log,
},
group: "id",
attributes: {
include: [
[sequelize.fn("COUNT", sequelize.col("record_date")), "order_count"],
],
},
having: sequelize.literal("`order_count` > 0"), // <-- Add this row
});
Does that help?

Convert SQL query to sequelize query with multiple tables

I've been at this for several days attempting to convert what I thought was a relatively simple SQL query into sequelize format. I cannot seem to figure it out for the life of me. I'm relatively new to sequelize, and my SQL skills can use some help as well.
Any help is greatly appreciated, thanks!
Here is the SQL query I have (which works for what I'm attempting to do) which I'm struggling to get to work in sequelize:
SELECT
book.id,
book.author,
book.title,
book_type.type,
book_sub_type.sub_type,
book_location.location,
book_language.language
FROM book
INNER JOIN book_type ON book.book_type_id = book_type.id
INNER JOIN book_sub_type ON book.book_sub_type_id = book_sub_type.id
INNER JOIN book_location ON book.book_location_id = book_location.id
INNER JOIN book_language ON book.book_language_id = book_language.id
WHERE
book.author LIKE '%history%' OR
book.title LIKE '%history%' OR
book_type.type LIKE '%history%' OR
book_sub_type.sub_type LIKE '%history%' OR
book_language.language LIKE '%history%' OR
book_location.location LIKE '%history%'
ORDER BY book_type.type, book_sub_type.sub_type;
Here is as far as I have gotten (this sequelize query returns 0 results because it is searching for the substring "history" in all columns, instead of at least one column):
const books = await Book.findAll({
where: {
[Op.or]: [
{author: { [Op.substring]: 'history' }},
{title: { [Op.substring]: 'history' }}
]
},
attributes: ['id', 'author', 'title'],
include: [
{
model: BookType,
attributes: ['type'],
where: {
type: { [Op.substring]: 'history' }
}
},
{
model: BookSubType,
attributes: ['sub_type'],
where: {
sub_type: { [Op.substring]: 'history' }
}
},
{
model: BookLanguage,
attributes: ['language'],
where: {
language: { [Op.substring]: 'history' }
}
},
{
model: BookLocation,
attributes: ['location'],
where: {
location: { [Op.substring]: 'history' }
}
},
]
});
My schema is as follows:
`book` table columns:
`id`, `author`, `title`, `book_type_id`, `book_sub_type_id`,
`book_language_id`, `book_location_id`
`book_type` table columns:
`id`, `type`
`book_sub_type` table columns:
`id`, `sub_type`
`book_location` table columns:
`id`, `location`
`book_language` table columns:
`id`, `language`
In sequelize, I have the following relationships established:
Book.belongsTo(BookType);
Book.belongsTo(BookSubType);
Book.belongsTo(BookLanguage);
Book.belongsTo(BookLocation);
BookType.hasMany(Book);
BookSubType.hasMany(Book);
BookLanguage.hasMany(Book);
BookLocation.hasMany(Book);
The output should be 7 columns:
book.id, book.author, book.title, book_type.type, book_sub_type.sub_type, book_location.location, book_language.language
Sequelize build a SQL with a conditions in JOINs, so this is not good aproach. You should remove all where conditions from includes. There was a way in a sequelize <4.0.0 to write conditions to subquery using syntax
where: {
$or: [{
'$book.sub_type$$': 'history'
}, {
'$book_type.type$': 'history'
}]
}
but I think this is not longer supported. Only way would be a custom query or use a sequelize literal in where object.
where: {
[Op.or]: [{
Sequelize.literal(`book_type.type LIKE ${history}`)
}, {
Sequelize.literal(`book_sub_type.sub_type LIKE ${history}`)
}]
}
Keep in mind that with this approach there is a risk of SQL injection so you should validate an input or use some escape character strategy. Check sequelize raw queries and seqeulize literal

How can I convert this raw query to sequelize ORM

The relationship between models is as:
I have three tables Staff,Modules,Tests
Staff (many-to-many) Modules
Modules (one-to-many) Tests
This is my raw query:
select *,count(distinct tests.testId),count(distinct staffModules.id) from modules
left join tests on modules.moduleId = tests.moduleModuleId
left join staffModules on modules.moduleId = staffModules.moduleModuleId
group by modules.moduleId
LIMIT 5 OFFSET 0
I have tried replicating it with Sequelize ORM but It doesn't work:
let resultModules = await modules.findAll({
attributes: ['moduleName'],
include: [{
model: staff,
attributes:['staffName'],
through: { attributes: [[sequelize.fn('COUNT', sequelize.col('staffStaffId')), 'staffCount']] },
},
{
model: tests,
attributes:['testsTaken','testsCompleted','testName'],
}
],
limit:parseInt(limit),offset:parseInt(offset),
});
Thank you for your time.
Maybe use possibility of raw queries for situations like this? ( http://docs.sequelizejs.com/manual/tutorial/raw-queries.html ) [ I would comment it, just don't have enough reputation ]
Try following Attributes in modules findAll -
attributes: [
"moduleName",
[Sequelize.literal("(SELECT count(DISTICT testId) FROM tests T WHERE (CONDITION) )"), "testIdCount"]
]
Only showing for test count can add the attribute for staffModules.

Sequelize Query Group by day objects found by parent property

So, I'm using sequelize with a mysql instance and I have this hierarchy : a task has n images and also n metadata key value pairs.
I want to get all images based on userId column of task, and afterwards get them grouped by 'createdAt' column taking into consideration the day, since a normal groupby will be pointless ( no objects share the same datetime ). I did lots of test to try to group, and I ended up using this query, which gives bad results ( I have like 11 images for a task, and it retrieves 4 ). Honestly, i don't know what I'm doing wrong. Any of you have any idea?
This is the code snippet:
var userId = security.utils.getKeycloakSubject(req);
var where = {
userId: userId
};
db.image.findAll({
include: [{
model: db.task,
include: [{
model: db.metadata,
as: 'metadata'
}],
where: where
}],
group: [db.Sequelize.fn('DAY', db.Sequelize.col('image.createdAt'))]
}).then(function (images) {
return res.json(images);
}, function (error) {
return res.status(500).json(error);
})
I saw your question and also found this: Sequelize grouping by date, disregarding hours/minutes/seconds
It is a question about group the DAY(createdAt), looks similar with yours.
And my solution of GROUP BY DAY() is look like:
item.findAll({
attributes:
[[sequelize.fn('DATE_FORMAT', sequelize.col('TimeStamp'), '%H'), 'dates']],
group: [sequelize.fn('DAY', sequelize.col('TimeStamp'))]
}).
then(function(result){console.log(result)
}).
catch(function(error){}).catch(function(error) {
console.log(error);
});
So the raw SQL likes: SELECT DATE_FORMAT('TimeStamp', '%H') as 'dates' FROM tableName GROUP BY DAY('TimeStamp');
Hope it helps you, or you can show us the SQL you want to use, maybe it is easier to help you too.
Good luck.

Specifying specific fields with Sequelize (NodeJS) instead of *

Alright so I have a project in NodeJS where I'm utilizing Sequelize for a MySQL ORM. The thing works fantastically however I'm trying to figure out if there is a way to specify what fields are being returned on a query basis or if there's even a way just to do a .query() somewhere.
For example in our user database there can be ridiculous amounts of records and columns. In this case I need to return three columns only so it would be faster to get just those columns. However, Sequelize just queries the table for everything "*" to fulfill the full object model as much as possible. This is the functionality I'd like to bypass in this particular area of the application.
You have to specify the attributes as a property in the object that you pass to findAll():
Project.findAll({attributes: ['name', 'age']}).on('success', function (projects) {
console.log(projects);
});
How I found this:
The query is first called here: https://github.com/sdepold/sequelize/blob/master/lib/model-definition.js#L131
Then gets constructed here: https://github.com/sdepold/sequelize/blob/master/lib/connectors/mysql/query-generator.js#L56-59
Try this in new version
template.findAll({
where: {
user_id: req.params.user_id
},
attributes: ['id', 'template_name'],
}).then(function (list) {
res.status(200).json(list);
})
Use the arrays in the attribute key. You can do nested arrays for aliases.
Project.findAll({
attributes: ['id', ['name', 'project_name']],
where: {id: req.params.id}
})
.then(function(projects) {
res.json(projects);
})
Will yield:
SELECT id, name AS project_name FROM projects WHERE id = ...;
All Answers are correct but we can also use include and exclude as well
Model.findAll({
attributes: { include: ['id'] }
});
Model.findAll({
attributes: { exclude: ['createdAt'] }
});
Source