Sequelize : Get Subquery/Raw Query as model for include - node.js

I have gone through the Sequelize doc, but can't find anything helpful
What I want to do is to add raw query or custom model in include, is it possible ?
model.Post.findAll({
include: [{
model: model.User,
attributes: ['fullname', 'profilepic'],
},
{
model: model.PostComments,
},
{
model: "Raw Query"
}
]
}
What I want to achieve is like :
Select post_id, count(*) as total_likes from post_likes group by post_id
I can't achieve this by using simply include, so what I want to do is create a table/model from above query and then use it inside include.
If I use group by withing include it gives group by from top level, and I want to apply group by just for the post_like table.
Please let me know, if it found confusing or not clear.

I was looking to do the same, use a raw query dynamically formed inside the include but there's no possible way to use include without a model https://sequelize.org/master/class/lib/model.js~Model.html#static-method-findAll .
For the purposes of my MySQL I turned my inner join (include) into a where in. I was doing the inner join to avoid the exception This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery . I get this exception because I have a subquery with LIMIT applied.
If it helps anyone:
let rows = await j_model.findAll({
attributes: [...j_columns, `${association}.meta_key_id`],
include: [
{
model: um_model,
as: association,
attributes: ['um_id'],
on: {'j_id' : {$col: 'j.j_id'} }
],
where: {'j_id': {$in: sequelize.literal(`(SELECT * FROM (${massive_inner_raw_query}) as tmp)`)}},
logging: console.log
});
The actual magic is in this line:
where: {'j_id': {$in: sequelize.literal(`(SELECT * FROM (${massive_inner_raw_query}) as tmp)`)}}
The SELECT * FROM removes that exception and lets me do a where in instead of the wanted INNER JOIN. Maybe you can apply a similar deal to your problem.

You can use
Model.findAll({
attributes: [[models.sequelize.literal('CASE WHEN "field1" = true THEN 55
ELSE 23 END'), 'field3']]
}
OR
Model.findAll({
attributes: { include: [[models.sequelize.literal('CASE WHEN "field1" = true THEN 55 ELSE 23 END'), 'field3']]}
}

Related

Get specific attributes from database using sequelize model query

I have red the documentation of sequelize but could not get the idea/concept i wanted ,I don't want just one attribute to be shown.
We have the following syntax to get the attributes we need
Model.findAll({
attributes:['foo','bar]
})
In my case I have many attributes in single table , I just want to hide one attribute.Is there any way in sequelize that we define the attribute which we don't want to see and get all other by default..
For Example....
Model.findAll({
attributes:hide foo , show all other attributes
})
AnyOne Can help..
You can use below syntax.
Model.findAll({
attributes: {
exclude: ['foo'] // hide this
}
});
You can also exclude fields on model level by adding in their default scope like below.
const Model = sequelize.define('model',{
secretColumn: Sequelize.STRING,
//... and other columns
}, {
defaultScope: {
attributes: { exclude: ['secretColumn'] }
}
});

Sequelize - order and exclude

I have a table of items with foreign keys for two tables, and I try to do FindAll with order, limit, exclude and include, something like this:
Items.findAll({
order: [['creationDate', 'DESC']],
limit: 10,
include: [
{
model: Artists,
required: false,
attributes: ['id', 'name',]
},
{
model: Albums,
required: false,
},
],
attributes: { exclude: ['creationDate'] }
})
But I get an error:
DatabaseError [SequelizeDatabaseError]: (conn = 837, no: 1054, SQLState: 42S22) Unknown column 'Items.creationDate' in 'order clause'
If I delete this line:
attributes: { exclude: ['creationDate'] }
The error disappears.
What should I do to enable the query with the desired attributes?
Thanks in advance
findAll with include tries to combine data from multiple tables. In SQL, Subqueries and JOIN both achieves this use case. However, there is a slight difference in how data is combined and that what you are seeing here. By default, Sequelize generates Subqueries for findAll + include and subQuery params is to tell Sequelize use Subquery (true) or JOIN (false).
For more about Subqueries vs JOIN: https://www.essentialsql.com/what-is-the-difference-between-a-join-and-subquery/#:~:text=Joins%20versus%20Subqueries,tables%20into%20a%20single%20result.&text=Subqueries%20can%20be%20used%20to,for%20use%20in%20a%20query
Back to Sequelize,
subQuery: true (default)
This will generate SQL like following
SELECT ...
FROM (SELECT ...(attributes except creationDate) FROM `Items` ...)
LEFT OUTER JOIN `Artists` ...
...
ORDER BY `Items`.`creationDate` DESC
LIMIT 10;
This SQL doesn't work, since nested Subquery is executed first and the Subquery is excluding creationDate and when main SQL with ORDER is executed, creationDate does not exist.
Then if you turn off Subquery.
subQuery: false
This will generate SQL like following
SELECT ... (Artist's, Album's attributes and Item's attributes except creationDate)
FROM `Items`
LEFT OUTER JOIN `Artists` ...
...
ORDER BY `Items`.`creationDate` DESC
LIMIT 10;
This will work because this won't filter attributes when ORDER is executed and exclude happens at top level.

Sequelize - JOIN query table with AND operator

I'm using sequelize to handle SQLite database used by Electon app. Application let the user search for music based on selected topics, moods and so on.
I'm trying to build search mechanism that allow to select multiple moods and the function should return tracks that have all of the selected moods
Here is mentioned above database simplified model:
Also the sequelize relation between models are set
db.moods.belongsToMany(db.tracks, {
through: db.moodsTracks,
foreignKey: 'uuid',
});
db.tracks.belongsToMany(db.moods, {
through: db.moodsTracks,
foreignKey: 'trackId',
});
db.moods.hasMany(db.moodsTracks, {foreignKey: 'uuid'});
db.tracks.hasMany(db.moodsTracks, {foreignKey: 'trackId'});
Now I'm trying to find all tracks that contain has specific moods
let tracks = await db.tracks.findAll({
include: [{
model: db.moods,
required: true,
where: uuid: {
[Op.and]: ['MOOD-UUID-1', 'MOOD-UUID-2']
}
}],
})
(first try fail)
I have tried to log generated by sequelize code and its returns:
INNER JOIN `moodsTracks` AS `moodsTracks` ON `tracks`.`id` = `moodsTracks`.`trackId`
AND (
`moodsTracks`.`uuid` = 'MOOD-UUID-1'
AND `moodsTracks`.`uuid` = 'MOOD-UUID-2'
)
Then I have try to build raw SQLite query
SELECT
COUNT(trackid),
*
FROM
`tracks` AS `tracks`
INNER JOIN `moodstracks` AS `moodsTracks` ON `tracks`.`id` = `moodstracks`.`trackid`
WHERE
(
`moodsTracks`.`uuid` = 'MOOD-UUID-1'
OR `moodsTracks`.`uuid` = 'MOOD-UUID-2'
)
GROUP BY
(`moodsTracks`.`trackId`)
HAVING
COUNT(trackid) = 2;
I'm aware that isn't great solution, but it works in SQL console.
Questions:
Is there any other way to solve that kind problem? Maybe I use AND operator wrongly
If not, I will try to implement that SQL code above.
Is there any documentation for HAVING keyword in sequelize, i didn't found any thing like this on official web page
Why are you using [Op.and] when in your raw query you are using 'OR'?
This solution works for me:
let tracks = await db.tracks.findAll({
include: [db.moods],
group: 'moodsTracks.trackId',
include: [{
model: db.moods,
where: {
uuid:['UUID1', 'UUID2']
},
having: ["COUNT(DISTINCT moodsTracks.uuid) = 2"]
}]
})

checks on associated models from top model where query

I have a model Booking, which is having hasMany relation with hotels, and hotel is having one to one relation with supppliers.
What i need is, get all booking where supplier_id = 33333.
I am trying this
BOOKINGS.findAll({
where: {
'hotels.supplier.supplier_id' : '32',
},
include: [
{
model: HOTELS,
include: [
{
model: SUPPLIERS,
],
}
],
limit : 30,
offset: 0
})
It throws error like hotels.supplier... column not found.. I tried all things because on docs of sequelze it only gives solution to add check which adds where inside the include which i can't use as it adds sub queries.
I don't want to add where check alongwith supplier model inside the include array, because it adds sub queries, so If i am having 1000 bookings then for all bookings it will add sub query which crashes my apis.
I need a solutions like this query in Sequelize.
Select col1,col2,col3 from BOOKINGS let join HOTELS on BOOKINGS.booking_id = HOTELS.booking_id, inner join SUPPLIERS on BOOKINGS.supplier_id = SUPPLIERS.supplier_id
Adding a where in the include object will not add a sub query. It will just add a where clause to the JOIN which is being applied to the supplier model. It will not crash your API in anyway. You can test it out on your local machine plenty of times to make sure.
BOOKINGS.findAll({
include: [
{
model: HOTELS,
include: [
{
model: SUPPLIERS,
where: { supplier_id: 32 }
}
]
}
],
limit: 30,
offset: 0
})
If you still want to use the query on the top level you can use sequelize.where+ sequelize.literal but you will need to use the table aliases that sequelize assigns. e.g this alias for supplier table will not work hotels.supplier.supplier_id. Sequelize assings table aliases like in the example I have shown below:
BOOKINGS.findAll({
where: sequelize.where(sequelize.literal("`hotels->suppliers`.supplier_id = 32")),
include: [
{
model: HOTELS,
include: [SUPPLIERS]
}
],
limit: 30,
offset: 0
})

Having trouble adding a where clause to a join table using Sequelizer for NodeJS

I am executing the following where. The key logic that is causing me problems is in line #7 where I'm trying to specify a where condition on a join table.
models.Portfolio.findAll({
include: [{
model: models.PortfolioPermissions,
}],
where: models.sequelize.or(
{'userId': userId},
{'PortfolioPermissions.userId': userId}
),
order: [['startDate', 'DESC']]
})
You can see the resulting query below has a major problem in line #9. Sequelize is prepending my where clause with portfolios table which is messing everything up.
SELECT `portfolios`.*,
`sharedUsers`.`id` AS `sharedUsers.id`,
`sharedUsers`.`permissions` AS `sharedUsers.permissions`,
`sharedUsers`.`userId` AS `sharedUsers.userId`,
`sharedUsers`.`portfolioId` AS `sharedUsers.portfolioId`
FROM `portfolios`
LEFT OUTER JOIN `portfolioPermissions` AS `sharedUsers` ON `portfolios`.`id` = `sharedUsers`.`portfolioId`
WHERE (`portfolios`.`userId`=1
OR `portfolios`.`PortfolioPermissions.userId`=1)
ORDER BY `startDate` DESC;
If someone could please point me in the right direction here, I'd really appreciate it. Thank you so much. I'm using Postgres FYI, probably not relevant though.
Condition on a joined table should be put in the relevant include entry, not on the global where condition.
In your case something like the following should work:
models.Portfolio.findAll({
include: [{
model: models.PortfolioPermissions,
where: {'userId': userId}
}],
where: models.sequelize.or(
{'userId': userId}
),
order: [['startDate', 'DESC']]
})

Resources