sequelize join up the tables - node.js

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

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 to convert postgreSQL to Sequelize format

I have to try to convert my postgre sql query to orm concept using sequelize npm package, kindly guide me.
select * from "locationDepartmentMappings" as a
inner join "departments" as b on a."departmentId" = b.id
inner join "locations" as c on a."locationId" = c.id
where (
b."departmentName" like '%Accounting%' or c."locationName" like '%Accounting%'
)
limit 1;
As per below code still i have getting
error: column locationDepartmentMapping.department.departmentName does not exist
As #shivam mentioned i have tried depends mine below, can you what i need to changes,
let ldv = await LocationDepartmentModel.findAll({
include: [
{
model: LocationModel,
as: "location",
required: true,
},
{
model: DepartmentModel,
as: "department",
required: true,
}
],
where: {
$or: [
{
"department.departmentName": { like: `%${reqQueryName}%` }
},
{ "location.locationName": { like: `%${reqQueryName}%` } }
]
},
limit:1
});
Sequelize has pretty good documentation on how to write queries using the orm syntax.
To start with, the above query would look something like
Model.locationDepartmentMappings.findAll({
include: [
{
model: Model.departments
where: {departmentName: {$like: '% Accounting%'}}
},
{
model: Model.locations
where: {locationName: {$like: '% Accounting%'}}
}],
limit: 1
})
What you should consider getting to above query is
1. Learn how to create sequelize models
2. Learn Associations
3. Querying
There are a lot of great tutorials out there, which can help you get started, and are just a google search away!
Final with help got the solutions :
let ldData = await LocationDepartmentModel.findAll({
include: [
{
model: LocationModel,
as: "location",
required: true
},
{
model: DepartmentModel,
as: "department",
required: true
}
],
where: {
$or: [
{ "$department.departmentName$": { like: `%${reqQueryName}%` } },
{ "$location.locationName$": { like: `%${reqQueryName}%` } }
]
},
limit: 1
});
Courtesy for below :
#shivam answer for joining tables
#ManjulSigdel answer for where condition include table column

Sequelize - query with or operator and model association

With Sequelize, I have two models with many to many association : User and Category.
I want to get all categories that belongs to the current user, and also categories with a certain property, but I don't understand how, with only one query...
I'm using the Op.or operator, according to the documentation, and the $Model.attribute$ syntax for associated model (seen here).
let categories = await models.category.findAll({
where: {
[Op.or]: [
{ someCategoryProperty: true },
{ '$User.id$': req.currentUser.id },
],
},
include: [{
model: models.user,
as: 'User',
}],
});
The operator works if I add 2 conditions about the Category model, but how to add a condition on the association ?
I finally found the tips :
Actually, $Model.attribute$ wasn't the good pattern, $database_table_name.attribute$ is the good one.
With the '$..$' syntax, we must use the database table name, and not the model.
If my model is called user, Sequelize set the database name users !
So this code works :
let categories = await models.category.findAll({
where: {
[Op.or]: [
{ someCategoryProperty: true },
{ '$users.id$': req.currentUser.id },
],
},
include: [{
model: models.user,
}],
});
Thanks

Sequelize and querying on complex relations

I'm trying to understand how best to perform a query over multiple entities using Sequelize and Node.js.
I have defined a model "User" which has a belongsToMany relation with a model "Location". I then have a model "Asset" which also has a belongsToMany relation with "Location". When I have an instance of a User I would like to fetch all Assets that are associated with Locations that the User is associated with.
I tried the following which doesn't seem to work...
user.getLocations().then(function(userLocations) { return Asset.findAll({ where: { "Locations" : { $any : userLocations } }) })
Could anyone offer any suggestions?
Try this query:
User.findById(user_id, {
include: [{
model: Location,
required: true
}]
}).then(user => Asset.findAll({
where: {
user_id: user.id,
location_id: {
$in: user.locations.map(location => location.id)
}
}
})).then(assets => {
// The rest of your logic here...
});
This was the final result...
User.findById(user_id, {
include: [{
model: Location,
as: 'Locations', // Was needed since the original relation was defined with 'as'
required: true
}]
}).then(user => Asset.findAll({
include: [{
model: Location,
as: 'Locations',
where: {
id: {
$in: user.Locations.map(location => location.id)
}
}
}]
})).then(assets => {
// The rest of your logic here...
});

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