Sequelize Include, join, limit associated tables - sql

I have a problem with the sequelize query, I think they should give the same rerult, but they don't. Can someone please explain this situation?
The first case:
getLatestStation = (stationId) => {
return models.MonitoringDataInfo.findAll({
where: {stationId :stationId},
attributes: ['id', 'stationId', 'sentAt'],
order : [['sentAt', 'DESC']],
limit: 1,
include : [{model: models.MonitoringData, attributes: ['idData', 'indicator', 'value', 'unit', 'sensorStatus']}]
})
}
The first result:
The first query:
SELECT [MonitoringDataInfo].*, [MonitoringData].[id] AS [MonitoringData.id], [MonitoringData].[idData] AS [MonitoringData.idData], [MonitoringData].[indicator] AS [MonitoringData.indicator], [MonitoringData].[value] AS [MonitoringData.value], [MonitoringData].[unit] AS [MonitoringData.unit], [MonitoringData].[sensorStatus] AS [MonitoringData.sensorStatus] FROM (SELECT [MonitoringDataInfo].[id], [MonitoringDataInfo].[stationId], [MonitoringDataInfo].[sentAt] FROM [monitoring_data_info] AS [MonitoringDataInfo] WHERE [MonitoringDataInfo].[stationId] = N'mkkneh3uBrAkw9hepMlF' ORDER BY [MonitoringDataInfo].[sentAt] DESC OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY) AS [MonitoringDataInfo] LEFT OUTER JOIN [monitoring_data] AS [MonitoringData] ON [MonitoringDataInfo].[id] = [MonitoringData].[idData] ORDER BY [MonitoringDataInfo].[sentAt] DESC;
The second case:
getLatestStation = (stationId) => {
return models.Station.findAll({
attributes: ['id', 'name', 'address', 'envIndex'],
where : {
id : stationId,
publicStatus : 1
},
include : [{
model: models.MonitoringDataInfo,
limit: 1,
// where : {
// stationId: stationId
// },
attributes: ['id', 'stationId', 'sentAt'],
order : [['sentAt', 'DESC']],
limit : 1,
include : [{model: models.MonitoringData, attributes: ['idData', 'indicator', 'value', 'unit', 'sensorStatus']}]
}]
})
}
The second result:
The second query:
SELECT [Station].[id], [Station].[name], [Station].[address], [Station].[envIndex] FROM [stations] AS [Station] WHERE [Station].[id] = N'mkkneh3uBrAkw9hepMlF' AND [Station].[publicStatus] = 1;
SELECT [MonitoringDataInfo].[id], [MonitoringDataInfo].[stationId], [MonitoringDataInfo].[sentAt], [MonitoringData].[id] AS [MonitoringData.id], [MonitoringData].[idData] AS [MonitoringData.idData], [MonitoringData].[indicator] AS [MonitoringData.indicator], [MonitoringData].[value] AS [MonitoringData.value], [MonitoringData].[unit] AS [MonitoringData.unit], [MonitoringData].[sensorStatus] AS [MonitoringData.sensorStatus] FROM [monitoring_data_info] AS [MonitoringDataInfo] LEFT OUTER JOIN [monitoring_data] AS [MonitoringData] ON [MonitoringDataInfo].[id] = [MonitoringData].[idData] WHERE [MonitoringDataInfo].[stationId] IN (N'mkkneh3uBrAkw9hepMlF') ORDER BY [MonitoringDataInfo].[sentAt] DESC, [MonitoringDataInfo].[id] OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY;

The first query does not limit records from MonitoringData in contrast to second one.
Try to indicate separate:true in include:
include : [{model: models.MonitoringData, separate: true, attributes: ['idData', 'indicator', 'value', 'unit', 'sensorStatus']}]

Related

Sequelize - Subselect association to filter parent model

I've recently started to use Sequelize 6. Currently trying to express the following query unsuccessfully.
SELECT u.*
FROM users u
JOIN requests r
ON u.id = r.user_id
WHERE (r.status = true AND r.created_at IN (SELECT
MAX(r.created_at)
FROM users u
INNER JOIN requests r ON r.user_id = u.id
GROUP BY u.id))
GROUP BY u.id, r.id
What I have tried so far:
const result = await User.findAll({
offset: page,
subQuery: false,
limit: rowsPerPage,
where: {
'$requests.status$': true,
},
include: [{
as: 'requests',
model: Request,
separate: true,
required: true,
order: [['createdAt', 'desc']],
limit: 1, // problem is here -> error: missing FROM-clause entry for table "requests"
}],
});
In other words, I'm trying to load the parent model only when the association's status value is true on the last record, and return each individual user row alongside with its latest association.
Regarding the limit problem on my comment, this thread might be useful
https://github.com/sequelize/sequelize/issues/11617
I had to change original SQL a little bit to work with Sequelize because a certain combination of required, where didn't work well.
So, I went off from your description.
I'm trying to load the parent model only when the association's status
value is true on the last record
If I understand this correctly, I think you can use having query to achieve this.
const result = await User.findAll({
offset: page,
subQuery: false,
limit: rowsPerPage,
include: [{
as: 'requests',
model: Request,
required: true
}],
having: {
'$requests.createdAt$': [sequelize.fn('MAX', sequelize.col('`requests`.`createdAt`'))],
'$requests.status$': true
},
group: ['users.id'],
});
This will produce a following SQL. Please try this SQL and let me know if this is what you are looking for or not.
SELECT ... FROM `users` AS `users`
INNER JOIN `requests` AS `requests`
ON `users`.`id` = `requests`.`userId`
GROUP BY `users`.`id`
HAVING `requests`.`createdAt` IN (MAX(`requests`.`createdAt`)) AND `requests`.`status` = 1
LIMIT 0, 5;

Select sql query to group by nested json

The data I am getting from select query is something like this
[{
id: 'CB2FD8B7-7E6D-4BF3-8E73-70D41FFBE456',
products: '[{"product_id":"22061DA1-5D64-475A-B36A-140ECFE8D6B7"}]',
falconpay_api_response: null,
dhl_updated_by: null
}, ... ]
What I am doing is fetching orders and then parsing products attribute and extracting product_id and counting the number of time the product_id occured in the number of different json objects.
This would be very time consuming if number of rows to fetch are in thousands and then extracting ids and counting its occurrences.
Is there any effective way to use GROUP by and get directly occurrences of product_id for thousands of rows.
You can use it!
var data = [{
id: 'CB2FD8B7-7E6D-4BF3-8E73-70D41FFBE451',
products: [{"product_id":"22061DA1-5D64-475A-B36A-140ECFE8D6B7"}],
falconpay_api_response: null,
dhl_updated_by: null
},{
id: 'CB2FD8B7-7E6D-4BF3-8E73-70D41FFBE452',
products: [{"product_id":"22061DA1-5D64-475A-B36A-140ECFE8D6B7"},{"product_id":"22061DA1-5D64-475A-B36A-140ECFE8D6K7"}],
falconpay_api_response: null,
dhl_updated_by: null
},{
id: 'CB2FD8B7-7E6D-4BF3-8E73-70D41FFBE453',
products: [{"product_id":"22061DA1-5D64-475A-B36A-140ECFE8D6K7"}],
falconpay_api_response: null,
dhl_updated_by: null
},{
id: 'CB2FD8B7-7E6D-4BF3-8E73-70D41FFBE454',
products: [{"product_id":"22061DA1-5D64-475A-B36A-140ECFE8D6B7"}],
falconpay_api_response: null,
dhl_updated_by: null
}];
var a =[];
data.forEach(function(order){
order.products.forEach(function(product){
if(a[product.product_id] == undefined){
a[product.product_id] = [];
}
a[product.product_id].push(order);
})
});
console.log(a);
Try the following Query (That's only works in JSON data type) :-
SELECT products->>'$.product_id' products,
count(products)
FROM events
GROUP BY products->>'$.product_id';
And Following Link will helpful to you :-
[http://www.mysqltutorial.org/mysql-json/]

Sequelize nested include with required: true generates invalid join

I apologise for the lengthy post in advance!
I am trying to use sequelize in nodeJs to query a Wordpress mysql database with a required: true in a nested include.
However, the generated query includes a bad join (I'd expect the join to be nested like the nested where clause). I don't seem to be able to figure out if I've configured my schema incorrectly or whether I am just doing something else silly.
Any help would be greatly appreciated!
In essence, my schema is:
const Post = sequelize.define('wp_posts', {
ID: {
type: DataType.BIGINT,
primaryKey: true
}
});
const TermRelationship = sequelize.define('wp_term_relationships', {
object_id: {
type: DataType.BIGINT,
primaryKey: true,
allowNull: false
},
term_taxonomy_id: {
type: DataType.BIGINT,
primaryKey: true,
allowNull: false
}
});
const TermTaxonomy = sequelize.define('wp_term_taxonomy', {
term_taxonomy_id: {
type: DataType.BIGINT,
primaryKey: true
}
});
const Term = sequelize.define('wp_terms', {
term_id: {
type: DataType.BIGINT,
primaryKey: true
}
});
The relationships I have defined are:
Post.belongsToMany(TermTaxonomy, {
through: TermRelationship,
otherKey: 'term_taxonomy_id',
foreignKey: 'object_id',
as: 'termTaxonomies'
});
TermTaxonomy.belongsTo(Term, {
foreignKey: 'term_id',
as: 'term'
});
The query I am executing is
const query = {
limit: 1,
include: [
{
model: TermTaxonomy,
required: true,
as: 'termTaxonomies',
include: [
{
model: Term,
as: 'term',
required: true,
}
]
}
]
};
However, the generated query includes a bad join. Here is the generated query. I have included comments where I see the errors:
SELECT
`wp_posts`.*,
`termTaxonomies`.`term_taxonomy_id` AS `termTaxonomies.term_taxonomy_id`,
`termTaxonomies`.`term_id` AS `termTaxonomies.term_id`,
`termTaxonomies`.`taxonomy` AS `termTaxonomies.taxonomy`,
`termTaxonomies`.`description` AS `termTaxonomies.description`,
`termTaxonomies`.`parent` AS `termTaxonomies.parent`,
`termTaxonomies`.`count` AS `termTaxonomies.count`,
`termTaxonomies->wp_term_relationships`.`object_id` AS `termTaxonomies.wp_term_relationships.object_id`,
`termTaxonomies->wp_term_relationships`.`term_taxonomy_id` AS `termTaxonomies.wp_term_relationships.term_taxonomy_id`,
`termTaxonomies->wp_term_relationships`.`term_order` AS `termTaxonomies.wp_term_relationships.term_order`
FROM
(
SELECT
`wp_posts`.`ID`,
`wp_posts`.`post_author`,
`wp_posts`.`post_date_gmt`,
`wp_posts`.`post_content`,
`wp_posts`.`post_title`,
`wp_posts`.`post_excerpt`,
`wp_posts`.`post_status`,
`wp_posts`.`comment_status`,
`wp_posts`.`ping_status`,
`wp_posts`.`post_password`,
`wp_posts`.`post_name`,
`wp_posts`.`to_ping`,
`wp_posts`.`pinged`,
`wp_posts`.`post_modified_gmt`,
`wp_posts`.`post_content_filtered`,
`wp_posts`.`post_parent`,
`wp_posts`.`guid`,
`wp_posts`.`menu_order`,
`wp_posts`.`post_type`,
`wp_posts`.`post_mime_type`,
`wp_posts`.`comment_count`,
-- ERROR
-- wp_terms cannot be joined to wp_posts
`termTaxonomies->term`.`term_id` AS `termTaxonomies.term.term_id`,
`termTaxonomies->term`.`name` AS `termTaxonomies.term.name`,
`termTaxonomies->term`.`slug` AS `termTaxonomies.term.slug`,
`termTaxonomies->term`.`term_group` AS `termTaxonomies.term.term_group`
FROM
`wp_posts` AS `wp_posts`
-- ERROR: bad join!
-- wp_terms cannot be joined to wp_posts
INNER JOIN `wp_terms` AS `termTaxonomies->term` ON `termTaxonomies`.`term_id` = `termTaxonomies->term`.`term_id`
WHERE
(
SELECT
`wp_term_relationships`.`object_id`
FROM
`wp_term_relationships` AS `wp_term_relationships`
INNER JOIN `wp_term_taxonomy` AS `wp_term_taxonomy` ON `wp_term_relationships`.`term_taxonomy_id` = `wp_term_taxonomy`.`term_taxonomy_id`
INNER JOIN `wp_terms` AS `wp_term_taxonomy->term` ON `wp_term_taxonomy`.`term_id` = `wp_term_taxonomy->term`.`term_id`
WHERE
(
`wp_posts`.`ID` = `wp_term_relationships`.`object_id`
)
LIMIT
1
) IS NOT NULL
LIMIT
1
) AS `wp_posts`
INNER JOIN (
`wp_term_relationships` AS `termTaxonomies->wp_term_relationships`
INNER JOIN `wp_term_taxonomy` AS `termTaxonomies` ON `termTaxonomies`.`term_taxonomy_id` = `termTaxonomies->wp_term_relationships`.`term_taxonomy_id`
) ON `wp_posts`.`ID` = `termTaxonomies->wp_term_relationships`.`object_id`;
The error I get is 'Unknown column 'termTaxonomies.term_id' in 'on clause' due to the incorrect join.
The generated query is valid if I either remove required: true or the limit option since it no longer does the strange inner join. However, I just can't seem to get this to work with a required nested include.
FYI: I am using sequelize 4.37.10 with 1.5.3 mysql2 1.5.3.
Thank you very much!
When eager loading, we can force the query to return only those records which have an associated model, effectively converting the query from the default OUTER JOIN to an INNER JOIN. Just remove required:true it will work fine generally this happens when we eager load, probably in your case we are eagerloading.
I hope you find this explanation beneficial.

Is this SQL possible in Sequelize?

I've just started with sequelize and am trying to reproduce the below query.
I have the following Model structure: Review, Entity, ReviewThank
Each Entity can have many Reviews, and each Review can have many ReviewThanks.
An attribute of each review is a 'thumbUp' (boolean) rating.
I'm trying to generate the below query to get a 'thankCount' for each review, along with the Entity rating - thumbUpCount and totalCount - for each Review:
SELECT * FROM (
SELECT COUNT("Review"."id") AS "totalCount", "Review"."EntityId", COUNT(CASE WHEN "Review"."thumbUp" THEN 1 END) AS "thumbUpCount"
FROM "Reviews" AS "Review" GROUP BY "Review"."EntityId"
) AS "EntityRatingTable" LEFT JOIN (
SELECT "Review"."id", "Review"."EntityId", "Review"."uid", "Review"."thumbUp", "Review"."caption", COUNT("ReviewThanks"."id") AS "thankCount"
FROM "Reviews" AS "Review" LEFT OUTER JOIN "ReviewThanks" AS "ReviewThanks" ON "Review"."id" = "ReviewThanks"."ReviewId"
WHERE "Review"."UserId" IN (1) GROUP BY "Review"."id"
) AS "ReviewsTable" ON "ReviewsTable"."EntityId" = "EntityRatingTable"."EntityId";
Is it possible to produce this in sequelize? I've got the "ReviewsTable" query working ok, but unsure how (or if possible) I can join this with the "EntityRatingTable"?
This is what I've got so far:
models.Review.findAll({
attributes: {
include: [
[models.sequelize.fn('COUNT', models.sequelize.col('ReviewThanks.id')), 'thankCount'],
[models.sequelize.fn('COUNT', models.sequelize.col('Review.id')), 'reviewCount'],
],
exclude: ["EntityId", "UserId"],
},
include: [
{
model: models.ReviewThank,
attributes: [],
}, {
model: models.Entity,
}
],
group: ['"Review"."id"', '"Entity.id"'],
})

find using model association

Dialect: postgres
Database version: #latest
Sequelize version: #latest
I'm trying to find out how to use an associate model. I've got 3 models: post, postCity and region. They have the following relation:
postCity (post_id, region_id) associate to post (post_id) and region (region_id). I am using a search function like this:
include: [
{
model: models.postCity,
include:[{model:models.region}],
attributes: [[models.sequelize.fn('count', 'post_id'), 'count']],
}
],
where: {
$or: [
{
"create_by" : {$not: 67}
},
{
// "postCities.region_name":{$iLike: "%Guangazhou2%"}
},
{
"description":{$iLike: "%India%"}
}
]
}
which leads to:
SELECT "post"."post_id", "post"."description", "post"."create_by",
"post"."create_time", "post"."update_time", "post"."country_id",
"postCities"."post_id" AS "postCities.post_id",
"postCities"."region_id" AS "postCities.region_id",
"postCities"."order_no" AS "postCities.order_no",
"postCities.region"."region_id" AS "postCities.region.region_id",
"postCities.region"."region_name" AS "postCities.region.region_name",
"postCities.region"."country_id" AS "postCities.region.country_id",
"postCities.region"."province_id" AS "postCities.region.province_id"
FROM "t_post" AS "post"
LEFT OUTER JOIN "t_post_city" AS "postCities"
ON "post"."post_id" = "postCities"."post_id"
LEFT OUTER JOIN "t_region" AS "postCities.region"
ON "postCities"."region_id" = "postCities.region"."region_id"
WHERE ("post"."create_by" != 67 OR "post"."description" ILIKE '%India%');
When I uncomment "postCities.region_name":{$iLike: "%Guangazhou2%"} then I get this error
column post.postCities.region_name does not exist
I simply like to my query to be like this
... WHERE ("post"."create_by" != 67
OR "post"."description" ILIKE '%India%'
OR "postCities.region_name" ILIKE: "%Guangazhou2%")
Update
I also tried to include [{model:models.region, where:{"region_name":{$iLike: "%Guangazhou2%"}}}] but this doesn't give me the appropriate result.
In order to add condition to included tables, you should wrap condition with $ symbol, like it:
include: [{
model: models.postCity,
include:[{model:models.region}],
attributes: [[models.sequelize.fn('count', 'post_id'), 'count']],
}],
where: {
$or: [{
"create_by" : {$not: 67}
}, {
"$postCities.region.region_name$":{$iLike: "%Guangazhou2%"}
}, {
"description":{$iLike: "%India%"}
}]
}