how to merge response attributes of other table in node js - node.js

I have 3 Tables User, Cars and UserCars
User{id, name, phone, email}
Cars{id, name, manufacturer}
UserCars{id, car_id, user_id, role}
User have many cars(through UserCars)
Cars have many users(through UserCars)
I am using express js
router.get('/', async (req, res) => {
try {
let car = await Car.findOne({
where: {
id: req.car_id
}});
let users = await car.getUsers({joinTableAttributes: ['role']})
res.send(users)
} catch (e) {
console.log(e)
res.status(400).send(e)
}
})
and this my response
[
{
"id": 1,
"name": "test",
"email": null,
"phone": null,
"createdAt": "2019-07-09T09:38:11.859Z",
"updatedAt": "2019-07-12T04:34:20.922Z",
"User_car": {
"role": "driver"
}
}
]
but any idea how to include role in the user object, rather then specifying it separately in User_car table,
Is there a way where i can get the below output
[
{
"id": 1,
"name": "test",
"email": null,
"phone": null,
"role": 'driver'
"createdAt": "2019-07-09T09:38:11.859Z",
"updatedAt": "2019-07-12T04:34:20.922Z"
}
]

You can use sequelize.literal to get that field when getting your attributes.
attributtes: [
// define your other fields
[sequelize.literal('`users->User_car`.`role`'), 'role'],
]
Now, I not sure if that is going to work with car.getUsers. I usually do a single query with include and also define the "join" table so I can know how is name it on sequelize. Let me show you an example.
module.exports = (sequelize, DataTypes) => {
const UserCar = sequelize.define('UserCar', {
// id you don't need and id field because this is a N:M relation
role: {
type: DataTypes.STRING
},
carId: {
field: 'car_id',
type: DataTypes.INTEGER
},
userId: {
field: 'user_id',
type: DataTypes.INTEGER
},
}, {
tableName: 'User_car',
underscored: true,
createdAt: 'created_at',
updatedAt: 'updated_at',
});
UserCar.associate = (models) => {
models.user.belongsToMany(models.car, { as: 'cars', through: User_car, foreignKey: 'user_id' });
models.car.belongsToMany(models.user, { as: 'users', through: User_car, foreignKey: 'car_id' });
};
return UserCar;
};
router.get('/', async (req, res) => {
try {
const users = await User.findAll({
include: [{
model: Car,
as: 'cars',
where: { id: req.car_id }
}],
attributtes: [
'id',
'name',
'email',
'phone',
[sequelize.literal('`cars->User_car`.`role`'), 'role'],
]
})
res.send(users)
} catch (e) {
console.log(e)
res.status(400).send(e)
}
});

Related

Counting number of associated rows in sequelize

I am trying to find the count of students inside the classStudents association. Here teacher_details table is connected to class_details tables using 'section'
association and class_details tables is connected to student_details table using 'classStudents' association. I have tried the below method but got error ==>
Unknown column 'section->classStudents.studentId' in 'field list'
please let me know the correct procedure to count the associated students count
const getAssignedDetails = async (req, res) => {
try {
const assignedDetails = await Entity.teacherAssignedDetail.findAll({
where: { status: 1, teacherId: req.teacherId },
attributes: [
'role',
[
Sequelize.fn(
'COUNT',
Sequelize.col('section.classStudents.studentId')
),
'studentCount',
],
],
include: [
{
association: 'section',
attributes: ['sectionId', 'className', 'classEmoji'],
include: [
{
association: 'classStudents',
where: { status: 1 },
required: false,
},
],
where: { status: 1 },
},
{
association: 'subjectDetails',
attributes: [['id', 'masterSubjectId'], 'subjectName'],
},
],
});
res.json({
responseCode: 0,
responseMessage: 'Success',
responseData: assignedDetails,
});
} catch (err) {
res.status(500).send(errorHandling(err.message));
}
};

NodeJS + Sequelize / How to perform triple model query?

I come from this topic:
NodeJS & Sequelize: How can I join 3 models?
I come to you now, because I need someone who can teach me about how to get started on junction models with NodeJS and Sequelize.
What I'm trying to do is, having 2 main models, for example, Employee and Office, a 3rd model called EmployeeOffice enters and connects the 2 main models, including its own field called "employee_chair". When i call a method, for example, Office.findAll, this is what I would get:
{
"id": 1,
"office_name": "Mars"
"office_color": "Red",
"createdAt": "2021-09-30T18:53:38.000Z",
"updatedAt": "2021-09-30T18:53:38.000Z",
"employees": [
{
"id": 1,
"employee_name": "Albert",
"employee_mail": "qalbert443#gmail.com",
"createdAt": "2021-09-30T18:53:45.000Z",
"updatedAt": "2021-09-30T18:53:45.000Z",
"employee_office": {
"createdAt": "2021-09-30T18:53:45.000Z",
"updatedAt": "2021-09-30T18:53:45.000Z"
}
}
]
}
NOW. What I need, is to have the model called instead of the junction table that is automatically created. Because I can join the 3 tables, but the 3rd table has the field "employee_chair", that I mentioned earlier. The desired response would look like this:
{
"id": 1,
"office_name": "Mars"
"office_color": "Red",
"createdAt": "2021-09-30T18:53:38.000Z",
"updatedAt": "2021-09-30T18:53:38.000Z",
"employees": [
{
"id": 1,
"employee_name": "Albert",
"employee_mail": "qalbert443#gmail.com",
"createdAt": "2021-09-30T18:53:45.000Z",
"updatedAt": "2021-09-30T18:53:45.000Z",
"employee_office": {
"employee_chair": 3,
"createdAt": "2021-09-30T18:53:45.000Z",
"updatedAt": "2021-09-30T18:53:45.000Z"
}
}
]
}
How can I do to make (or force) sequelize to make the relations through the model and not through the automatically created table?
Hope you guys can help me, I'm stuck and I don't know where to ask
#cupid22 Here is my index.js, userproject model and the controller function im calling:
index.js:
const dbConfig = require("../config/db.config.js");
const Sequelize = require("sequelize");
const sequelize = new Sequelize(dbConfig.DB, dbConfig.USER, dbConfig.PASSWORD, {
host: dbConfig.HOST,
dialect: dbConfig.dialect,
operatorsAliases: false,
pool: {
max: dbConfig.pool.max,
min: dbConfig.pool.min,
acquire: dbConfig.pool.acquire,
idle: dbConfig.pool.idle
}
});
const db = {};
db.Sequelize = Sequelize;
db.sequelize = sequelize;
db.projects = require("./project.model.js")(sequelize, Sequelize);
db.users = require("./user.model.js")(sequelize, Sequelize);
db.projects.belongsToMany(db.users, {
through: "users_projects",
as: "users",
foreignKey: "user_id",
});
db.users.belongsToMany(db.projects, {
through: "users_projects",
as: "projects",
foreignKey: "project_id",
});
module.exports = db;
Controller function:
// Retrieve all Projects from the database.
exports.findAll = (req, res) => {
const title = req.query.title;
var condition = title ? { title: { [Op.like]: `%${title}%` } } : null;
Project.findAll({
include: [
{
model: User,
as: "users",
}]
})
.then(data => {
res.send(data);
})
.catch(err => {
res.status(500).send({
message:
err.message || "Some error occurred while retrieving projects."
});
});
};
UserProject Model:
module.exports = (sequelize, Sequelize) => {
const UserProject = sequelize.define('user_project', {
user_id: {
type: Sequelize.INTEGER,
references: {
model: 'user',
key: 'id'
}
},
project_id: {
type: Sequelize.INTEGER,
references: {
model: 'project',
key: 'id'
}
},
user_type: {
type: Sequelize.INTEGER,
}
}, {
timestamps: false,
tableName: 'users_projects'
});
db.users.belongsToMany(db.projects, { through: User_Profile });
db.projects.belongsToMany(db.users, { through: User_Profile });
return UserProject;
};

join same table in sequelize and print in excel

I want to print parent name in excel
this is my code
function getReportT0Print(req, res) {
return new Promise((resolve, reject) => {
Product.findAll({
where: {
$and: [
{
public: true,
visible: true,
ancestry: {
$not: null,
},
},
],
},
include: [
{
model: ormDb.Document,
required: false,
},
],
attributes: ["name", "slug", "folder_path"],
})
.then(function (data) {
// console.log("data" + data.length);
var rows = [];
rows.push(["Product Name", "Slug", "File Path", "Product Parent Name"]);
data.map(function (product) {
rows.push([
product.name,
product.slug,
product.folder_path,
(here i need to print parent name)
]);
});
var workbook = new Excel.Workbook();
var sheet = workbook.addWorksheet("products_with_tags");
sheet.addRows(rows);
resolve(workbook);
return res.send("successfull");
})
.catch(function (err) {
reject(err);
});
});
}
i can print name, slug,folder_path but i don't know how to print parent name in excel file
as parent name is not present but i have given parent_id in place of parent name and want to print parent name
my SQl table look like this
("id" "name" "version" "published" "reviewed" "visible" "public", "parent_id")
You need to register an association between two Product models like this:
Product.belongsTo(Product, { foreignKey: 'parent_id', as: 'parent' });
You can place it in some module outside model's module file where you create Sequelize instance, for example.
To use this association you need to use the same include option as you did with Document:
Product.findAll({
where: {
$and: [
{
public: true,
visible: true,
ancestry: {
$not: null,
},
},
],
},
include: [
{
model: Product,
as: 'parent',
required: false, // or true if parent_id is required field
// or you wish to get all products with parents
attributes: ["name", "slug", "folder_path"],
},
{
model: ormDb.Document,
required: false,
},
],
attributes: ["name", "slug", "folder_path"],
})

SequelizeJS, Is this a best way to create this JSON Result using this models

I want this result, but, is so complicated do to this in this way. There is a better way to create this result using Sequelize. Using the querys tools of sequelize to aggregate results from diferent tables, on JPA the only thing i do is annotate with join table and pass the columns ad invese columns values.
[
{
"id": 1,
"codemp": "999",
"nome": "A3 Infortech",
"limiteInstancias": "10",
"instancias": []
},
{
"id": 2,
"codemp": "92",
"nome": "Endovideo",
"limiteInstancias": "20",
"instancias": [
{
"id": 198211,
"ipLocal": "40.0.10.11",
"ipExterno": "187.33.230.106",
"hostname": "FATURAMENTO-PC",
"dataCriacao": "2019-07-01T21:40:29.000Z"
}
]
},
{
"id": 6,
"codemp": "103",
"nome": "SOS Otorrino",
"limiteInstancias": "999",
"instancias": [
{
"id": 199127,
"ipLocal": "192.168.11.101",
"ipExterno": "000.000.000.000",
"hostname": "Guiche3-PC",
"dataCriacao": "2019-07-01T21:40:32.000Z"
},
{
"id": 199164,
"ipLocal": "192.168.25.209",
"ipExterno": "000.000.000.000",
"hostname": "Consultorio06",
"dataCriacao": "2019-07-01T21:40:29.000Z"
}
]
},
{
"id": 15,
"codemp": "162",
"nome": "Clinica Vida e Saude",
"limiteInstancias": "10",
"instancias": [
{
"id": 199774,
"ipLocal": "192.168.56.1",
"ipExterno": "000.000.000.000",
"hostname": "ALEXANDRELEAL",
"dataCriacao": "2019-07-01T21:40:28.000Z"
}
]
}
]
I have this codes:
Empresa Model
module.exports = (sequelize, DataTypes) => {
const empresa = sequelize.define("empresa", {
id: {
type: DataTypes.BIGINT(20),
primaryKey: true,
field: "id"
},
codemp: {
type: DataTypes.INTEGER,
field: "codemp"
},
nome: {
type: DataTypes.STRING,
field: "nome"
},
limiteInstancias: {
type: DataTypes.INTEGER,
field: "limite_instancias"
}
}, {
timestamps: false,
freezeTableName: true,
tableName: "empresa"
});
empresa.associate = (db) => {
console.log(db);
empresa.hasMany(db.instanciaEmpresa, {foreignKey: "id_empresa"});
};
return empresa;
};
Instancia Model
module.exports = (sequelize, DataTypes) => {
const instancia = sequelize.define("instancia", {
id: {
type: DataTypes.BIGINT(20),
primaryKey: true,
field: "id"
},
ipLocal: {
type: DataTypes.STRING,
field: "ip_local"
},
ipExterno: {
type: DataTypes.STRING,
field: "ip_externo"
},
hostname: {
type: DataTypes.STRING,
field: "hostname"
},
dataCriacao: {
type: DataTypes.DATE,
field: "data_criacao"
},
}, {
timestamps: false,
freezeTableName: true,
tableName: "instancia"
});
instancia.associate = (db) => {
console.log(db);
instancia.belongsTo(db.empresa, {foreignKey: "id_instancia"});
};
return instancia;
};
InstanciaEmpresa Model
module.exports = (sequelize, DataTypes) => {
const instanciaEmpresa = sequelize.define("instancia_empresa", {
idEmpresa: {
type: DataTypes.BIGINT(20),
primaryKey: true,
field: "id_empresa"
},
idInstancia: {
type: DataTypes.BIGINT(20),
primaryKey: true,
field: "id_instancia"
},
}, {
timestamps: false,
freezeTableName: true,
tableName: "instancia_empresa"
});
return instanciaEmpresa;
};
My Database diagram.
A picture of my database diagram
The code of my response
const db = require("../config/db.config");
const empresa = db.empresa;
const instancia = db.instancia;
const instanciaEmpresa = db.instanciaEmpresa;
const empressaResult = [];
module.exports = {
async getAll(req, res) {
return res.send(await getAllEmpresa());
}
};
async function getAllEmpresa() {
//Recover all companies from the table
let empresaList = await empresa.findAll({raw: true});
//I browse the array of companies to retrieve the instances associated with the company
for(let i = 0; i < empresaList.length; i++){
//Create the atribute Instancias[]
empresaList[i].instancias = [];
//I retrieve the list of associated instances in the InstanciaEmpresa table
let instanciasEmpresa = await instanciaEmpresa.findAll({where: {"id_empresa": empresaList[i].id}, raw: true});
//Verify if existes any item of InstanciaEmpresa
if(instanciasEmpresa.length > 0){
//If there is a run through list of instances
for(let j = 0; j < instanciasEmpresa.length; j++){
//I retrieve the Instancia in the database and add it to the company Instancias list
let inst = await instancia.findByPk(instanciasEmpresa[j].idInstancia, {raw: true});
empresaList[i].instancias.push(inst);
}
}
//I add the company with the instances in a result list;
empressaResult.push(empresaList[i]);
}
return empressaResult;
}
You can use include option to operate join on your tables.
Then your code would look like,
const empresaList = await empresa.findAll({
raw: true,
include: [
{
model: instancias,
required: false, // left join, `true` means inner join.
}
]
});
As you can see, you can pass array of { model, required } into include option.
You can set required to true if you want to operate inner join else it would operate left join.
--- ADDED ---
SequelizeEagerLoadingError: instancia is not associated to empresa means you're not calling associate function on db initialization.
You can write helper function like below in your db.js.
addAssociations(name) {
if (this[name].associate) {
this[name].associate(this);
}
}
}
and use it like
/*
* this.models = [
* {
* name: 'instancias',
* model: instancias,
* },
* ... and many more
* ]
*/
this.models.forEach((value) => {
this.addAssociations(value.name);
});

Sequelize: How to make relationship between 3 tables

If you have the following entities: Users, Roles, Organizations.
You want to setup the relationships so that each user has an organization-role.
In simple each user can belong to multiple organizations and the user has a specific role in each organization.
How would you model this with Sequelize?
I have tried by creating a junction table called organisation_users and then in that table adding a organisationUsers.belongsTo(role);
From I have read Sequelize doesnt support associations on junction tables and so that solution doesn't work.
Regards,
Emir
Sequelize does supports associations on join table. You should look in the "through" options here >
Example :
const Asso_Organization_User = sequelize.define('Asso_Organization_User', {
id: DataTypes.STRING,
userId: DataTypes.STRING,
organizationId: DataTypes.STRING
});
User.Organizations = User.belongsToMany(Organization, {
through: Asso_Organization_User,
foreignKey: 'userId',
otherKey: 'organizationId',
as: 'organizations'
})
But your case is a bit special, I dont see a way using sequelize to get a user, all of its organizations, and his role for each organization in the same query.
It looks like there is no solution yet, based on this issue: https://github.com/sequelize/sequelize/issues/6671
Maybe you could do it in two queries : get all organizations and all roles, and then merging them with code.
Actually, I see another solution:
const Asso_Organization_User = sequelize.define('Asso_Organization_User', {
id: DataTypes.STRING,
userId: DataTypes.STRING,
organizationId: DataTypes.STRING
});
User.Organizations = User.belongsToMany(Organization, {
through: Asso_Organization_User,
foreignKey: 'userId',
otherKey: 'organizationId',
as: 'organizations'
})
The model would look like the following :
User: id
Role: id, userId, organizationId
Organizations: id
Asso_Organization_User: id, userId, organizationId
Then :
User.Organizations = User.belongsToMany(Organization, {
through: Asso_Organization_User
})
Organization.Roles = Organization.haMany(Role, {
foreignKey: 'organizationId'
})
And then you should be able to query :
User.findAll({
include: [ {
model: Organization,
include: {
model: Role
}
} ]
where: {
'role.userId': Sequelize.col("User.id")
}
});
I'm not totally sure of the exact syntax, but combining Sequelize.col with the hasMany should work. But be careful, if you dont add this where clause, this would return all roles for each organization (for every user having a role in this organization).
It may vary upon your requirements of fetching the data from these 3 tables
Consider this Example :
Tables : login, userProfile, farmer
Requirements : Farmer has one user Profile , User Profile has one Login.
Along with these tables we can register a user as a Farmer.
--------------------------------------------------------------------------
farmer.js
module.exports = (Sequelize, DataTypes) => {
const Farmer = Sequelize.define("farmer", { /*attributes*/});
return Farmer;
};
--------------------------------------------------------------------------
login.js
module.exports = (Sequelize, DataTypes) => {
const Login = Sequelize.define("login", {*attributes*/});
return Login;
};
--------------------------------------------------------------------------
userProfile.js
module.exports = (Sequelize, DataTypes) => {
const UserProfile = Sequelize.define("userProfile", {*attributes*/});
return UserProfile;
};
--------------------------------------------------------------------------
index.js
const dbConfig = require("../config/dbConfig"); // your config file
const { Sequelize, DataTypes } = require("sequelize");
//object initilize. (pass parameter to constructor)
const sequelize = new Sequelize(dbConfig.DB, dbConfig.USER, dbConfig.PASSWORD, {
host: dbConfig.HOST,
dialect: dbConfig.dialect,
operatorsAliases: false, //hide errors
pool: {
max: dbConfig.pool.max,
min: dbConfig.pool.min,
acquire: dbConfig.pool.acquire,
idle: dbConfig.pool.idle,
},
});
sequelize
.authenticate()
.then(() => {
console.log("DB connected!");
})
.catch((err) => {
console.log("Error " + err);
});
const db = {}; // Empty object
db.Sequelize = Sequelize;
db.sequelize = sequelize;
db.login = require("./login.js")(sequelize, DataTypes);
db.userProfile = require("./userProfile.js")(sequelize, DataTypes);
db.farmer = require("./farmer.js")(sequelize, DataTypes);
//relations
db.farmer.userProfile = db.farmer.belongsTo(db.userProfile);
db.userProfile.login = db.userProfile.belongsTo(db.login);
db.sequelize
.sync({ force: false }) //force :true - drop all tables before start
.then(() => {
console.log("yes-sync done!");
});
module.exports = db;
//Declare follwing things in a separate location (may be controllers__.js
-----------INSERT DATA (CREATE)--------------------------
const saved = await Farmer.create(
{
supplierCode: "SUP0001",
userProfile: {
firstName: "ssss",
middleName: "ssss",
lastName: "ssss",
address: "ssss",
login: {
name: "ssss",
email: "ssss",
password: "ssss",
role: "ssss",
lastLogin: null,
avatar: "ssss",
status: "ssss",
},
},
},
{
include: [
{
association: Farmer.userProfile,
include: [Login],
},
],
}
);
Observe the usage of the include option in the Farmer.create call. That is necessary for Sequelize to understand what you are trying to create along with the association.
Note: here, our user model is called farmer, with a lowercase f - This means that the property in the object should also be farmer. If the name given to sequelize.define was Farmer, the key in the object should also be Farmer.
-----------FETCH DATA (SELECT)--------------------------
const users = await Farmer.findAll({
include: [
{
association: Farmer.userProfile,
include: [Login],
},
],
});
Output:
[
{
"id": 1,
"supplierCode": "SUP0001",
"createdAt": "2021-11-17T07:39:13.000Z",
"updatedAt": "2021-11-17T07:39:13.000Z",
"userProfileId": 1,
"userProfile": {
"id": 1,
"firstName": "ssss",
"middleName": "ssss",
"lastName": "ssss",
"address": "ssss",
"createdAt": "2021-11-17T07:39:13.000Z",
"updatedAt": "2021-11-17T07:39:13.000Z",
"loginId": 1,
"login": {
"id": 1,
"name": "ssss",
"email": "ssss",
"password": "ssss",
"role": "ssss",
"lastLogin": null,
"avatar": "ssss",
"status": "ssss",
"createdAt": "2021-11-17T07:39:13.000Z",
"updatedAt": "2021-11-17T07:39:13.000Z"
}
}
}
]
Assume you want to add another user Role/Type (we have Farmer already). then you can make coordinater.js (example user role/type) and defind attributes
in above index.js you can add this relation
//Coordinator relation
db.coordinator.userProfile = db.coordinator.belongsTo(db.userProfile, {
onDelete: "CASCADE",
onUpdate: "CASCADE",
});
Now you can register users with different users Roles/Types :
Coordinator.create.... give correct associations

Resources