Inner join query in Sequelize referencing multiple tables - node.js

HOW DO I WRITE BELOW QUERY IN SEQUELIZE?
select vdc_id,vdc.name as vdc,
districts.name as district,
states.name as state,
countries.name as country
from vdc
INNER JOIN districts on (vdc.district_id=districts.district_id and vdc.name like 'L%')
INNER JOIN states on (districts.state_id=states.state_id)
INNER JOIN countries on (states.country_id=countries.country_id)
order by district asc;
Associations :
vdc table contains fk_district_id
db.vdc.belongsTo(db.districts,{
foreignKey:"district_id"
})
db.districts.hasMany(db.vdc,{
foreignKey:"district_id"
})
districts table contains fk_state_id
db.districts.belongsTo(db.states,{
foreignKey:"state_id"
})
db.states.hasMany(db.districts,{
foreignKey:"state_id"
})
states table contains fk_country_id
db.states.belongsTo(db.countries,{
foreignKey:"country_id"
})
db.countries.hasMany(db.states,{
foreignKey:"country_id"
})
THANKS YOU!

It seems you would need something like the code below. You would need to adjust it to work with your models since they were not provided, but this should give you an idea of what is required to get the query you want.
vdc.findAll({
attributes: ['vcd_id', 'name'],
include: [
{
model: 'districts', // Or districts model
attributes: [['name', 'district']], // name as district
required: true, // Use inner join instead of left join
on: { // Join on district id and name like 'L%'
'district_id': { [Op.eq]: sequelize.col('districts.district_id') },
'name': { [Op.like]: 'L%' }
}
},
// Custom join shouldn't be necessary for the following due to foreign key config
{
model: 'states',
attributes: [['name', 'state']],
required: true
},
{
model: 'countries',
attributes: [['name', 'country']],
required: true
}
],
order: [['district', 'ASC']]
});

Related

Sequelize join two tables on id

I have a table of book users and a table of movie users. I'm trying to return a list of the top 100 movie viewers, along with their book profile information. I want to join on ids, but I can't seem to find the right syntax.
This is what I've tried:
const mostActiveMovieWatchers = await MovieWatchers.findAll({
order: [
['moviesWatched', 'DESC'],
],
limit: '100',
include: [{
model: BookReaders,
where: {
userId: '$MovieWatchers.id$'
},
required: true
}]
});
I've also seen examples where the where clause looks something like this where: ['userId = id']
Before join tables you need create association:
BookReaders.hasMany(MovieWatchers, { foreignKey: 'bookId' });
MovieWatchers.belongsTo(BookReaders, { foreignKey: 'bookId' });
Then, you can use the include option in a find or findAll method call on the MovieWatchers model to specify that you want to include the associated BookReaders data:
MovieWatchers.findAll({
include: [
{
model: BookReaders,
},
],
}).then((movies) => {
// array of movies including books
});

How Can I write INNER Join query using Sequelize on Node.js

I have two tables. One is tblEmailAuditLog having columns Id, OrderId, Body, CommunicationType, CreatedOn, Reciever, WorkflowStatusId, IsSuccess another one is tblLookUp having column names as LU_LookUpValue and LU_LOOKUPID. I want to join the two tables based on the columns (CommunicationType and LU_LOOKUPID)
My inner join query looks as below:
select Id, OrderId, Body, CommunicationType, CreatedOn, Reciever, WorkflowStatusId, IsSuccess, LU_LookUpValue from tblEmailAuditLog INNER JOIN tblLookUp on tblEmailAuditLog.CommunicationType = tblLookUp.LU_LOOKUPID;
I am trying to write the above query using sequelize ORM.
I have tried the below things.
1.
let auditLogs = await MediMarket.TblEmailAuditLog.findAll({
include: [
{
model: MediMarket.TblLookUp,
required: true,
where: { orderId: req.body.orderId },
},
],
});
2.
let auditLogs = MediMarket.TblEmailAuditLog.hasMany(
MediMarket.TblLookUp,
{ foreignKey: "CommunicationType" }
);
MediMarket.TblLookUp.belongsTo(MediMarket.TblEmailAuditLog, {
foreignKey: "LU_LOOKUPID",
});
MediMarket.TblEmailAuditLog.findAll({
where: { orderId: req.body.orderId },
include: [MediMarket.TblLookUp],
});
But it is not giving the expected result.

sequelize get column value of referenced table

I have three tables meetings, rooms and projects
meetings.belongsTo(models.rooms, { foreignKey: "room_id" , targetKey: "id"});
rooms.hasMany(models.meetings, { foreignKey : "id", targetKey : "room_id"});
rooms.belongsTo(models.projects, { foreignKey: "project_id", targetKey: "id"});
projects.hasMany(models.rooms, {foreignKey:"id", targetKey:"project_id"});
id, room_id and project_id are primary key of their respective table.
I want to find value of some column of projects for a particular meeting id. How to write a single query using sequelize nodejs to do this?
below query need to execute using sequelize which is giving correct result
select project_meta from projects p
inner join rooms r on p.id = r.project_id
inner join meetings m on r.id = m.room_id
where m.id = "QBZJ0TK7V6NFSPPWGFNCN";
wrote following
projects.findAll({
include: [{
model: rooms,
include: [{
model: meetings,
where: {
id: "QBZJ0TK7V6NFSPPWGFNCN"
}
}]
}],
attributes: ['project_meta']
}
but it is executing different query and giving unexpected result
is there any problem with association?
You must use SQL joins (sequelize's include)
function myQuery(meetingId) {
let options = {};
options.attributes = ['column1', 'column2', ...]; // The specific arrtibutes you need.
options.include = [
{
model: RoomModel,
include: [
{
model: MeetingModel,
where: {
id: meetingId // The specific meeting id
}
}
]
}
];
ProjectModel.findAll(options);
}

Sequelize Nested Association with Two Tables

I have a scenario where I am trying to query a parent table (document) with two associated tables (reference & user) that do not have a relationship to each other, but do have a relationship with the parent table. In SQL, this query would look like such and correctly outputs the data I am looking for:
select *
from `document`
left join `user`
on `document`.`user_id` = `user`.`user_id`
left join `reference`
on `document`.`reference_id` = `reference`.`reference_id`
where `user`.`organization_id` = 1;
However, associations that are nested have to relate in hierarchical order in order for the query to work. Since the nested associations are not related to each other I get an association error. How can I avoid this error? Would required: false have any influence on this?
models.Document.findAll({
order: 'documentDate DESC',
include: [{
model: models.User,
where: { organizationId: req.user.organizationId },
include: [{
model: models.Reference,
}]
}],
})
Error:
Unhandled rejection Error: reference is not associated to user!
Associations:
Document:
associate: function(db) {
Document.belongsTo(db.User, {foreignKey: 'user_id'}),
Document.belongsTo(db.Reference, { foreignKey: 'reference_id'});;
}
Reference:
associate: function(db){
Reference.hasMany(db.Document, { foreignKey: 'reference_id' });
}
Should I just chain queries instead?
If you want to replicate your query (as closely as possibly) use the following query. Keep in mind that the where on the User include will only serve to remove matches on Document.user_id where the User.organization_id does not match, but the Document will still be returned. If you want to omit Documents where the User.organization_id does not match use required: true.
User <- Document -> Reference
models.Document.findAll({
// this is an array of includes, don't nest them
include: [{
model: models.User,
where: { organization_id: req.user.organization_id }, // <-- underscored HERE
required: true, // <-- JOIN to only return Documents where there is a matching User
},
{
model: models.Reference,
required: false, // <-- LEFT JOIN, return rows even if there is no match
}],
order: [['document_date', 'DESC']], // <-- underscored HERE, plus use correct format
});
The error is indicating that the User model is not associated to the Reference model, but there are only definitions for the Document and Reference models in your description. You are joining these tables in your query with the include option, so you have to make sure they are associated. You don't technically need the foreignKey here either, you are specifying the default values.
Add Reference->User association
associate: function(db) {
// belongsTo()? maybe another relationship depending on your data model
Reference.belongsTo(db.User, {foreignKey: 'user_id'});
Reference.hasMany(db.Document, { foreignKey: 'reference_id' });
}
It also looks like you probably set underscored: true in your model definitions, so your query should reflect this. Additionally if you want to perform a LEFT JOIN you need to specify required: false on the include, otherwise it is a regular JOIN and you will only get back rows with matches in the included model. You are also using the wrong order format, it should be an array of values, and to sort by model.document_date DESC you should use order: [['document_date', 'DESC']].
Proper query arguments
models.Document.findAll({
order: [['document_date', 'DESC']], // <-- underscored HERE, plus use correct format
include: [{
model: models.User,
where: { organization_id: req.user.organization_id }, // <-- underscored HERE
required: false, // <-- LEFT JOIN
include: [{
model: models.Reference,
required: false, // <-- LEFT JOIN
}]
}],
});
If you are still having trouble, try enabling logging by setting logging: console.log in your Sequelize connection, that will show you all the queries it is running in your console.
It seems to me that your problem might be the associations, trying to link back to your primary key on Documents instead of the columns 'user_id' and 'reference_id'. You didn't post the table attributes so I might have understood this wrong.
Association on documents are ok.
Documents
associate: function(db) {
Document.belongsTo(db.User, {foreignKey: 'user_id'}), //key in documents
Document.belongsTo(db.Reference, { foreignKey: 'reference_id'}); //key in documents
}
User
associate: function(db) {
User.belongsTo(db.Document, {
foreignKey: 'id', //Key in User
targetKey: 'user_id' //Key in Documents
}),
}
Reference
associate: function(db) {
Reference.belongsTo(db.Document, {
foreignKey: 'id', //Key in reference
targetKey: 'reference_id' //Key in Documents
}),
}
Also
For debbuging consider using logging so you can see the queries.
var sequelize = new Sequelize('database', 'username', 'password', {
logging: console.log
logging: function (str) {
// do your own logging
}
});

How to count a group by query in NodeJS Sequelize

In Rails I can perform a simple ORM query for the number of Likes a model has:
#records = Model
.select( 'model.*' )
.select( 'count(likes.*) as likes_count' )
.joins( 'LEFT JOIN likes ON model.id = likes.model_id' )
.group( 'model.id' )
This generates the query:
SELECT models.*, count(likes.*) as likes_count
FROM "models" JOIN likes ON models.id = likes.model_id
GROUP BY models.id
In Node Sequelize, any attempt at doing something similar fails:
return Model.findAll({
group: [ '"Model".id' ],
attributes: ['id', [Sequelize.fn('count', Sequelize.col('"Likes".id')), 'likes_count']],
include: [{ attributes: [], model: Like }],
});
This generates the query:
SELECT
Model.id,
count(Likes.id) AS likes_count,
Likes.id AS Likes.id # Bad!
FROM Models AS Model
LEFT OUTER JOIN Likes
AS Likes
ON Model.id = Likes.model_id
GROUP BY Model.id;
Which generates the error:
column "Likes.id" must appear in the GROUP BY clause or be used in an aggregate function
It's erroneously selecting likes.id, and I have no idea why, nor how to get rid of it.
This sequelize github issue looks totally like your case:
User.findAll({
attributes: ['User.*', 'Post.*', [sequelize.fn('COUNT', 'Post.id'), 'PostCount']],
include: [Post]
});
To resolve this problem we Need to upgrade to latest version of sequelize and include raw = true,
Here is How I had done after lot of iteration and off-course googling.
getUserProjectCount: function (req, res) {
Project.findAll(
{
attributes: ['User.username', [sequelize.fn('COUNT', sequelize.col('Project.id')), 'ProjectCount']],
include: [
{
model: User,
attributes: [],
include: []
}
],
group: ['User.username'],
raw:true
}
).then(function (projects) {
res.send(projects);
});
}
where my reference models are
//user
var User = sequelize.define("User", {
username: Sequelize.STRING,
password: Sequelize.STRING
});
//project
var Project = sequelize.define("Project", {
name: Sequelize.STRING,
UserId:{
type:Sequelize.INTEGER,
references: {
model: User,
key: "id"
}
}
});
Project.belongsTo(User);
User.hasMany(Project);
after migration ORM create 'Users' & 'Projects' table into my postgres server.
Here is SQL Query by ORM
SELECT
"User"."username", COUNT("Project"."id") AS "ProjectCount"
FROM
"Projects" AS "Project"
LEFT OUTER JOIN "Users" AS "User" ON "Project"."UserId" = "User"."id"
GROUP BY
"User"."username";
What worked for me counting column A and grouping by column B
const noListingsPerRetailer = Listing.findAll({
attributes: [
'columnA',
[sequelize.fn('COUNT', sequelize.col('columnB')), 'labelForCountColumn'],
],
group:["columnA"]
});

Resources