How to count a group by query in NodeJS Sequelize - node.js

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"]
});

Related

sequelize join up the tables

Working on an already setup db and also new to sequelize. I have like four tables
customer
library
books
excerpt
excerpt has redundant book_id from books. books has redundant library_id from library, and library has redundant customer_id from customer.
They were not declared foreign in db but its kinda acting the foreign key type and I make the association when I hit the orm functions.
My question:
I have a customer_id and book_id, and I have to fetch excerpt records based on book_id but have to go top the db to match the customer_id as well. (for multi tenancy)
the flow is: excerpt> book_id - books > library_id - library > customer_id - customer
I have written this code but its not working
async read_book_id(customer_id, book_id) {
const excerpts = await this.model.findAll({ // this.model being the excerpt model
where: { book_id: book_id},
include: [
{
model: this.db.Books,
association:
this.model.belongsTo(this.db.Books, {
foreignKey: 'book_id',
}),
where: { book_id: book_id},
include: [
{
model: this.db.Library,
association: this.model.belongsTo(this.db.Library, {
foreignKey: 'library_id',
}),
where: { customer_id: customer_id },
},
],
},
],
});
Basically this is something extended from this another code i wrote which is working for me. If I have to check only one level above thats working fine for me e.g
// reading books based on library_id
async read(customer_id, library_id) {
const books= await this.model.findAll({
where: {
library_id: library_id,
},
include: [
{
model: this.db.Library,
association: this.model.belongsTo(this.db.Library, {
foreignKey: 'library_id',
}),
where: { customer_id: customer_id },
},
],
});
}
This works fine for me.
Can you please tell how to run the first code block?
Assuming you already registered all associations just like I suggested in the comments above you need to indicate the correct models in include options along with correct conditions:
const excerpts = await this.model.findAll({ // this.model being the excerpt model
where: { book_id: book_id},
include: [
{
model: this.db.Books,
include: [
{
model: this.db.Library,
where: { customer_id: customer_id },
required: true
},
],
},
],
});```

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);
}

Inner join query in Sequelize referencing multiple tables

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']]
});

Resources