How to Perform Multiple Inner Joins in Sequelize Postgresql - node.js

Am newbie to RDBMS and Sequelize as well wanted to explore more in those now am struck up with JOINS. I don't know how to perform JOINS via SEQUELIZE. I have 3 tables USERS,ORDERS,PRODUCTS ORDERS table contains USERS,PRODUCTS primary key as its foreign key. Am attaching my model code below
User Model
const Sequelize = require('sequelize');
const sequelize = require('../config');
let Users = sequelize.define('users', {
id : {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
username: {
type: Sequelize.STRING,
},
password: {
type: Sequelize.STRING
}
});
module.exports = Users;
Products Model
const Sequelize = require('sequelize');
const sequelize = require('../config');
let products=sequelize.define('products', {
id : {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
category : {
type: Sequelize.STRING,
allowNull: false
},
name : {
type: Sequelize.STRING,
allowNull: false
},
price: {
type: Sequelize.INTEGER,
allowNull: false
}
});
module.exports= products;
Orders Model
const Sequelize = require('sequelize');
const sequelize = require('../config');
let users=require('./user');
let products=require('./product');
let orders=sequelize.define('orders', {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
user_id: {
type: Sequelize.INTEGER,
references: {
model: 'users',
key: 'id'
}
},
product_id: {
type: Sequelize.INTEGER,
references: {
model: 'products',
key: 'id'
}
},
price: {
type: Sequelize.INTEGER,
allowNull: false
}
});
module.exports= orders;
I want this following raw query to be performed via SEQUELIZE
SELECT * FROM ((orders INNER JOIN users ON users.id=orders.user_id) INNER JOIN products ON products.id=orders.product_id);
I have looked at the documentation but i couldn't figure out how to do it. ANy help is appreciated. Thanks

First thing you need to do is set up your Associations.
So lets break this up in to parts. We know that your ORDERS table contains the id for a USER and a PRODUCT. So this is how you would set up your associations for these tables.
I am assuming that a user has many orders. We make the associations in both directions.
User.hasMany(Orders, {foreignKey: 'user_id'});
Order.belongsTo(User, {foreignKey: 'user_id'});
You have the model correctly defined it seems.
Now in order to do a join, after setting up the associations, we want to set up a query to do joins for tables. Now keep in mind this would be done in your controller.
// Make sure you import your models up here to use below
export function getRequestsByWeek(req, res) {
return order.findAll({
include: [
{model: users, attributes: []}, // nothing in attributes here in order to not import columns from users
{model: products} // nothing in attributes here in order to not import columns from products
],
attributes: ['id'], //in quotes specify what columns you want, otherwise you will pull them all
// Otherwise remove attributes above this line to import everything.
})
.then(respondWithResult(res))
.catch(handleError(res));
}

let users=require('./user');
let products=require('./product');
export function getOrders(req, res) {
return order.findAndCountAll({
include: [
{model: users, required: true}, // true for INNER JOIN
{model: products, required: false} // false for LEFT OUTER JOIN
],
them all
})
}

Related

Sequelize Associate: User is not associated to Appointments

Basically I have a user model and appointment model. The two models are linked with a one-to-many relationship. The Appointment table has two columns that are associated with the user model. When ever I try to include the properties of the user table in appointment, I get the above error.
These are my model designs
Appointment Model
export default ({
sequelize
}:{
sequelize: Sequelize
}) => {
const Appointments: ModelDefined<AppointmentsAttribute, AppointmentsCreationAttributes> = sequelize.define('Appointments', {
appointmentId: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
primaryKey: true,
},
client: {
type: DataTypes.STRING,
allowNull: false,
references: {
model: 'Users',
key: 'uid'
}
},
serviceProvider: {
type: DataTypes.STRING,
allowNull: false,
references: {
model: 'Users',
key: 'uid'
}
},
})
return Appointments
}
User Model
export default ({
sequelize
}: {
sequelize: Sequelize
}) => {
const User: ModelDefined<UserAttribute, UserCreationAttributes> = sequelize.define('User', {
uid: {
type: DataTypes.STRING,
allowNull: false,
unique:true,
primaryKey: true
},
firstname: {
type: DataTypes.STRING,
allowNull: false,
},
lastname: {
type: DataTypes.STRING,
allowNull: false,
},
}
})
return User
}
I have associated the user and appointment models with
Users.hasMany(Appointments);
The code that I am trying to use to fetch the appointment data and include the corresponding user value is
db.appointment.findAll({
where: {
client: this.uid
},
include: 'Users'
})
Sequelize always use associations for models whose association method was called. i.e. if you call Model1.hasMany(Model2) then you can execute queries like:
Model1.findAll({
include: [{
model: Model2
}]
}
and NOT vice versa like this:
Model2.findAll({
include: [{
model: Model1
}]
}
If you wish to request appointment with users as an associated model then you need to add a reversed association from Appointments to Users like this:
Users.hasMany(Appointments);
Appointments.belongsTo(Users);

How to create an object of a relationship in sequelize?

Hey I need to create a relationship in sequelize. I have the model and is well created in the database.
I'll show you my models but it's not very relevant.
Seller Model
const Sellers = db.define("sellers", {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true,
},
name: Sequelize.STRING,
surname: Sequelize.STRING,
});
Sellers.hasMany(Clients);
module.exports = Sellers;
Client Model
const Client = db.define("clients", {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true,
},
name: Sequelize.STRING,
creationDate: Sequelize.DATE,
client_type: Sequelize.STRING,
});
module.exports = Client;
What I want to do is simply make a relation between the client and seller. In the database a SellerId is added in the client table because of sequelize hasMany() method. What I want to do is just be able to pass the id to the ORM when creating a client so it makes the relationship automatically to the seller table.
Sequelize Documentation about this
Thank you for taking your time to read this. I hope you can help me! Have a great day.
First of all I'd prefer to define a foreign key column in a model and indicate it in associations explicitly. And of course you need to add another association from clients to sellers - belongsTo and call both of associations outside models to be able to reference them to each other.
Client model file:
const Client = db.define("clients", {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true,
},
name: Sequelize.STRING,
creationDate: Sequelize.DATE,
client_type: Sequelize.STRING,
sellerId: {
type: Sequelize.INTEGER,
allowNull: false // or true if this association is optional
},
});
some database.js file where you should register all associations:
....
Sellers.hasMany(Clients, { foreignKey: 'sellerId' });
Clients.belongsTo(Sellers, { foreignKey: 'sellerId' });
And then you can create a client indicating a seller's id:
const seller = await Seller.findOne({
where: {
name: 'Peter'
}
})
const newClient = await Client.create({
name: 'John',
sellerId: seller.id,
// other fields here
})

Sequelize double relationships

I'm doing a node.js project that allows virtual trading between user.
A User can have many trades. Each Trade will have to contain 2 fk from User, which are the id of the seller as well as the buyer.
I'm using sequelize in node.js to build up the model.
const { Sequelize, DataTypes, Model } = require('sequelize');
import {sequelize} from '../database/connection.js';
const User = sequelize.define('User', {
// Model attributes are defined here
id:{
type: DataTypes.INTEGER(11),
allowNull: false,
autoIncrement: true,
primaryKey: true
},
}, {
// Other model options go here
sequelize, // We need to pass the connection instance
freezeTableName: true
});
module.exports = User
///////////////////////////////////////////
// Trade
const { Sequelize, DataTypes, Model } = require('sequelize');
import {sequelize} from '../database/connection.js';
const Trade = sequelize.define('Trade', {
// Model attributes are defined here
id:{
type: DataTypes.INTEGER(11),
allowNull: false,
autoIncrement: true,
primaryKey: true
},
isPaid:{
type: DataTypes.BOOLEAN,
defaultValue: true
}
// fk are in relationships.js
}, {
// Other model options go here
sequelize, // We need to pass the connection instance
freezeTableName: true
});
module.exports = Trade
/////////////////////////////////////
//relationships
User.hasMany(Trade,{
foreignKey: "offererId"
})
Trade.belongsTo(User);
//
User.hasMany(Trade,{
foreignKey: "receiverId"
})
Trade.belongsTo(User);
Is this a right solution?
Using a super M:N may be your most versatile option.
User.belongsToMany(User, { through: Trade, as: "offerer", foreignKey: "offererId"})
User.belongsToMany(User, { through: Trade, as: "receiver", foreignKey: "receiverId"})
Trade.belongsTo(User)
User.hasMany(Trade)
This allows you to use the auto-generated association methods and query user on user.

Problem setting up Sequelize association - query with 'include' is failing

I'm new to Sequelize and trying to test if an n:m association I set up between two models, User and Podcast, is working. When I try to run this query, I get some kind of DB error that isn't specific about what's wrong:
User.findOne({
where: { id: id },
include: [{ model: Podcast }]
});
Does anyone know what I'm messing up? I suspect there's something wrong in how I've set up the association, like I'm referencing the names of tables slightly incorrectly, but the migration to create the association worked.
Here's my User.js model file:
module.exports = (sequelize, DataTypes) => {
const User = sequelize.define('User', {
name: {
type: DataTypes.STRING,
allowNull: false
},
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true
},
photo: {
type: DataTypes.STRING
}
});
User.associate = function(models) {
// associations can be defined here
User.belongsToMany(models.Podcast, {
through: 'user_podcast'
});
};
return User;
};
And here's my Podcast.js file:
'use strict';
module.exports = (sequelize, DataTypes) => {
const Podcast = sequelize.define('Podcast', {
id: {
type: DataTypes.STRING,
primaryKey: true,
allowNull: false
},
title: {
type: DataTypes.STRING,
allowNull: false
},
thumbnail: {
type: DataTypes.STRING
},
website: {
type: DataTypes.STRING
}
});
Podcast.associate = function(models) {
// associations can be defined here
Podcast.belongsToMany(models.User, {
through: 'user_podcast'
});
};
return Podcast;
};
And here's the migration I ran to join the two tables:
'use strict';
module.exports = {
up: function(queryInterface, Sequelize) {
return queryInterface.createTable('user_podcast', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
userId: {
type: Sequelize.INTEGER,
references: {
model: 'Users',
key: 'id'
}
},
podcastId: {
type: Sequelize.STRING,
references: {
model: 'Podcasts',
key: 'id'
}
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: function(queryInterface, Sequelize) {
return queryInterface.dropTable('user_podcast');
}
};
And here's the project on Github for further reference:
https://github.com/olliebeannn/chatterpod
You don't need to create a migration for the M:N table. Now you have something wrong on your user_podcast model. If you are setting a M:N relation between to tables your primary key will be the combination between the foreign key from these two models. If you still want a single id primary key for your table, then you won't use belongsToMany instead use hasMany on user and podcast models pointing to a new model user_podcast.
As far as I see on your first query, it seems that you really need a M:N relation so you can define the model as you do with user and podcast like this:
module.exports = (sequelize, DataTypes) => {
const UserPodcast = sequelize.define('user_podcast', {
userId: {
// field: 'user_id', #Use 'field' attribute is you have to match a different format name on the db
type: DataTypes.INTEGER
},
podcastId: {
// field: 'podcast_id',
type: DataTypes.INTEGER
},
});
UserPodcast.associate = function(models) {
models.User.belongsToMany(models.Podcast, {
as: 'podcasts', //this is very important
through: { model: UserPodcast },
// foreignKey: 'user_id'
});
models.Podcast.belongsToMany(models.User, {
as: 'users',
through: { model: UserPodcast },
// foreignKey: 'podcast_id'
});
};
return UserPodcast;
};
I do prefer to have the belongsToMany associations on the save function where I define the join model, and you have to notice that I used as: attribute on the association. This is very important because this will help sequelize to know which association are you referring on the query.
User.findOne({
where: { id: id },
include: [{
model: Podcast,
as: 'podcasts' //here I use the previous alias
}]
});

Sequelize: Querying where I use foreignKey instead of as

So I'm having some trouble getting the query results I'm looking for. In terms of models I have
models.UserRole = sequelize.define('userrole', {
name: {
type: Sequelize.STRING,
primaryKey: true
},
permissions: {
type: Sequelize.ARRAY(Sequelize.STRING),
defaultValue: []
}
});
models.User = sequelize.define('user', {
id: {
type: Sequelize.UUID,
defaultValue: Sequelize.UUIDV4,
primaryKey: true
},
...
});
models.User.belongsTo(models.UserRole, {foreignKey: 'role'});
And I used foreignKey instead of as because I wanted the field to be called role exactly and not what Sequelize was changing it to (roleName).
Anyway, I'm now trying to query and include the permissions along with the selected user, so I use
models.User.findById(id, {
attributes: ['id','role'],
include: [{
model: models.UserRole,
attributes: ['name', 'permissions']
}]
})
And it works, but it retrieves them on the field userrole and looks like this
{"id":"id-here","role":"admin","userrole":{"name":"admin", "permissions":["p1","p2",..]}}
So finally, my question is how do I make it so that the stuff it retrieves from the UserRole model is retrieved under the key "role" instead? So it looks like
{"id":"id-here","role":{"name":"admin", "permissions":["p1","p2",..]}}

Resources