Sequelize set associated value - node.js

I have two models, Employees and Offices. Every Employee belongs to one Office, and an Office has many Employees.
I am having difficulty figuring out how to update an Employee's office using Sequelize.
The Employee model is as follows:
let Employee = sequelize.define("Employee", {
id: {
field: 'id',
type: DataTypes.INTEGER,
primaryKey: true,
allowNull: false,
autoIncrement: false
},
name: {
field: 'name',
type: DataTypes.STRING(255),
allowNull: false
}
}, {
freezeTableName: true,
timestamps: false,
deletedAt: false
})
Employee.associate = models => {
Employee.belongsTo(models.Office, {
foreignKey: 'id'
})
}
The Office model is as follows:
let Office = sequelize.define("Office", {
id: {
field: 'id',
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
name: {
field: 'name',
type: DataTypes.STRING(255),
allowNull: false
}
}, {
freezeTableName: true,
tableName: 'Lkup_Office',
timestamps: false,
deletedAt: false
})
Office.associate = models => {
Office.hasMany( models.Employee, {
foreignKey: 'id'
})
}
In the database I have the following Employee:
{
"id": "2",
"name" : "John",
"office": {
"id": 2,
"name" : "Seattle"
}
}
... and the following Offices:
[
{
"id": 1,
"name" : "Chicago"
},
{
"id": 2,
"name" : "Seattle"
}
]
I want to change the ID of Employee(1)'s office from 2 ('Seattle') to 1 ('Chicago'); the problem is that with the following query
// PUT /2/office/1
router.put('/:employeeId/office/:officeId', (req, res) => {
models.Employee.findOne({
where:{id:req.params.employeeId},
include:[{model:models.Office}]
}).then( employee => {
models.Office.findOne({
where:{id:req.params.officeId},
include:[{model:models.Employee}]
}).then( office => {
employee.setOffice( office ).then( result => {
res.send( result )
})
})
})
})
... my Employee's office is not updated:
{
"id": "2",
"name" : "John"
"office": {
"id": 2
"name" : "Seattle"
}
}
It doesn't, in fact, do anything at all: no errors, the DB isn't changed. I have a suspicion that there's something I'm not doing correctly, but Sequelize isn't throwing any errors.

router.put('/:employeeId/office/:officeId', (req, res) => {
/*In this first part, I just separated the two queries into two
separate variables*/
let employeeInfo = models.Employee.findOne({
where: {
id: req.params.employeeId
},
include: [{
model: models.Office
}]
});
let officeInfo = models.Office.findOne({
where: {
id: req.params.officeId
},
include: [{
model: models.Employee
}]
});
/*In this next part, we have to use Promise.all, which comes with
bluebird, which is already a part of Sequelize.js. How this next
part is executed will depend on how you have your Sequelize.js code
structured in your application. This may require some trial and error*/
models.sequelize.Promise.all([employeeInfo, officeInfo]).then(([Employee, Office])=> {
return Employee.setOffice(Office);
}).catch(err => {
console.log(err);
});
});

Related

NodeJs Join two tables using sequalize and retrieve the data in Controller

I have two model classes in separate files as below,
module.exports = (sequelize, Sequelize) => {
return sequelize.define(
"course",
{
id: {
type: Sequelize.STRING,
primaryKey: true,
field: 'ID',
},
title: {
type: Sequelize.STRING,
field: 'TITLE'
}
},
{
timestamps: false,
freezeTableName: true,
underscored: true
},
);
};
module.exports = (sequelize, Sequelize) => {
return sequelize.define(
"student",
{
id: {
type: Sequelize.STRING,
primaryKey: true,
field: 'ID',
},
courseId: {
type: Sequelize.STRING,
field: 'COURSE_ID'
}
},
{
timestamps: false,
freezeTableName: true,
underscored: true
},
);
};
And in the controller I have written like this.
const db = require("../config/sequelize.config");
const course = db.course;
const student= db.student;
student.belongsTo(course, {foreignKey: 'courseId', targetKey: 'id'});
exports.findStudentData = (req, res) => {
return student.findOne({ limit: 1 },
{
include : [{
model: course
}]}).then(data => {
res.send(data);
}).
catch(err => {
res.status(500).send({
message:
err.message || "Some error occurred."
});
});
};
Here I need to get course data along with student data. When I ran the code it only gives me the student data without course details. I'm not sure If I have added the following statement correct
student.belongsTo(course, {foreignKey: 'courseId', targetKey: 'id'});

Pulling associated data fields in controller API's with Sequelize / Node

I have built a REST api to pull user log data in my Node backend. What is the best course of action to get the fields data of the associated tables? Would I have to manually add to my API the code to pull the data from other tables or is there a easier way of doing this with sequelize? Does this mean the way I have coded my foreign keys are incorrect?
The ID fields below are the ones I need to text data relating to the Int values as they won't mean anything to a user viewing a table on the front-end.
Note: I have the fields data in my Redux store on the front-end so I could also do something there If that is the most common process. I thought that the "include: {all: true}" part would pull the foreign key data but it hasn't worked.
"data": [
{
"diveID": 46,
"diveTypeID": 1,
"diveSchoolNameID": 4,
"diveCurrentID": 1,
"diveVisibilityID": 1,
"diveDate": "2020-01-09T00:00:00.000Z",
"diveMaxDepth": 15,
"diverUserNumber": 6,
"diveVerifiedBySchool": false,
"diveNotes": "Cold, poor view, miserable, seen catfish, eels",
"diveSpotID": null,
"divePhotos": null
},
{
"diveID": 47,
"diveTypeID": 1,
"diveSchoolNameID": 4,
"diveCurrentID": 1,
"diveVisibilityID": 1,
"diveDate": "2020-01-09T00:00:00.000Z",
"diveMaxDepth": 15,
"diverUserNumber": 6,
"diveVerifiedBySchool": false,
"diveNotes": "Cold, poor view, miserable, seen catfish, eels",
"diveSpotID": null,
"divePhotos": null
},
diveLog
module.exports = (sequelize, Sequelize) => {
const diveLog = sequelize.define("diveLogs", {
diveID: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true,
allowNull: false
},
diveTypeID: {
type: Sequelize.INTEGER
},
diveSchoolNameID: {
type: Sequelize.INTEGER
},
diveCurrentID: {
type: Sequelize.INTEGER
},
diveVisibilityID: {
type: Sequelize.INTEGER
},
diveDate: {
type: Sequelize.DATE
},
diveMaxDepth: {
type: Sequelize.INTEGER
},
diverUserNumber: {
type: Sequelize.INTEGER
},
diveVerifiedBySchool: {
type: Sequelize.BOOLEAN
},
diveNotes: {
type: Sequelize.TEXT
},
diveSpotID: {
type: Sequelize.INTEGER
},
divePhotos: {
data: Sequelize.Buffer,
type: Sequelize.TEXT
}},
{
timestamps: false
}, {});
diveLog.associate = function(models){
diveLog.belongsTo(models.diveRegion, {foreignKey: 'diveRegionID', as: 'diveRegion'})
diveLog.belongsTo(models.certification, {foreignKey: 'certificationID', as: 'certification'})
diveLog.belongsTo(models.current, {foreignKey: 'currentID', as: 'current'})
diveLog.belongsTo(models.visibility, {foreignKey: 'visibilityID', as: 'visibility'})
diveLog.belongsTo(models.divePoint, {foreignKey: 'diveSpotID', as: 'diveSpot'})
};
return diveLog;
};
controller method
exports.userDiveLog = (req, res) => {
try {
const id = req.params.id;
diveLog.findAll({
include: {all: true},
where: {diverUserNumber: id}
})
.then(diveLog => {
const userDiveLogList = [];
for (i = 0; i < diveLog.length; i++) {
userDiveLogList.push(diveLog[i].dataValues);
}
if (!userDiveLogList) {
return res.status(404).send({message: "No dive logs belonging to this user"});
}
res.status(200).send({
data: userDiveLogList
})
})
} catch(err) {
res.status(500).send({
message: "Error retrieving dive log belonging to user id= " + id
});
}
};

findAll sequelize with model returning odd values

Solved thanks to : https://stackoverflow.com/a/64702949/14585149
I am looking to list all transactions I have on a mysql database with the associated users.
I have 2 tables / models : Transaction & User
Transaction :
module.exports = function(sequelize, DataTypes) {
return sequelize.define('transactions', {
transaction_id: {
type: DataTypes.INTEGER(11),
allowNull: false,
primaryKey: true
},
user_id: {
type: DataTypes.INTEGER(11),
allowNull: false,
references: {
model: 'users',
key: 'user_id'
}
},
account_id: {
type: DataTypes.INTEGER(11),
allowNull: false,
references: {
model: 'accounts',
key: 'account_id'
}
},
}, { tableName: 'transactions' }); };
User :
module.exports = function(sequelize, DataTypes) {
return sequelize.define('users', {
user_id: {
type: DataTypes.INTEGER(11),
allowNull: false,
primaryKey: true,
references: {
model: 'recipients',
key: 'created_by'
}
},
contact_id: {
type: DataTypes.INTEGER(11),
allowNull: true,
references: {
model: 'contacts',
key: 'contact_id'
}
}, }, {
tableName: 'users',
timestamps: false
});
};
I have made the associations :
Transaction.hasOne(User, {foreignKey:'user_id'});
User.belongsTo(Transaction, {foreignKey:'user_id'});
and my code is :
api.get('/trx', async (req, res) => {
Transaction.findAll({
attributes: ['transaction_id','user_id','account_id'],
include: [{
model: User,
attributes: ['user_id']}]
})
.then(intrx => res.json(intrx))
.catch(res.catch_error)
});
the result :
[
{
"transaction_id": 1,
"user_id": 4,
"account_id": 1,
"user": {
"user_id": 1
}
},
{
"transaction_id": 2,
"user_id": 4,
"account_id": 75,
"user": {
"user_id": 2
}
}
]
why the values of user_id are different ?
I am expecting the user_id = 4 instead of 1 in my first result and user_id = 4 in my second result.
what I am doing wrong ?
You should reverse your associations like this:
Transaction.belongsTo(User, {foreignKey:'user_id'});
User.hasOne(Transaction, {foreignKey:'user_id'});
because Transaction has link to User i.e. belongs to it.

Sequelize: Bind multiple models for join query and create custom columns

I'd like to apply a join and groupBy in Sequelize v5 that will fetch the records from five models tables and return the records as below format.
{
"data": [
{
"products": {
"id": "01",
"name": "lorium",
"description": "ipsum",
"product_images": [
{
"url": "", // From images tbl
"image_type": "Front" // From imge_types tbl
},
{
"url": "",
"image_type": "Back"
}
]
},
"vendor": {
"first_name": "john",
"last_name": "doe"
}
}
]
}
I have created separate all five models and assign association to them.
Product Model::
const Product = SQLize.define('product', {
id: { type: DataTypes.INTEGER.UNSIGNED, autoIncrement: true, primaryKey: true, }
product_title: { type: new DataTypes.STRING(255) },
vendor_id: { type: DataTypes.INTEGER.UNSIGNED }
});
Product.hasMany(ProductImages, {foreignKey: 'product_id', targetKey: 'id', as :'product_img_refs'})
export { Product };
Vendor Model::
const Vendor = SQLize.define('vendor', {
id: { type: DataTypes.INTEGER.UNSIGNED, autoIncrement: true, primaryKey: true, },
first_name: { type: DataTypes.STRING(100) },
last_name: { type: DataTypes.STRING(100) }
});
Product.belongsTo(Vendor, {foreignKey: 'id'})
Vendor.hasOne(Product, {foreignKey: 'id'})
export { Vendor }
Product Images Model::
const ProductImages = SQLize.define('product_images', {
id: { type: DataTypes.INTEGER.UNSIGNED, autoIncrement: true, primaryKey: true, },
product_id: { type: DataTypes.INTEGER },
product_image_id: { type: DataTypes.INTEGER }
img_type_id: { type: DataTypes.INTEGER }
});
export {ProductImages}
Images Model::
const ImagesModel = SQLize.define('images', {
id: { type: DataTypes.INTEGER.UNSIGNED, autoIncrement: true, primaryKey: true, },
img_url: { type: DataTypes.STRING }
});
export { ImagesModel }
Image Types Model::
const ImageTypes = SQLize.define('image_types', {
id: { type: DataTypes.INTEGER.UNSIGNED, autoIncrement: true, primaryKey: true, },
image_type: { type: DataTypes.STRING }
});
export { ImageTypes }
Below is the repository file on which i have performed the SQLize operation: Updated::
public async getProductData() {
var prodData = Product.findAll({
include: [
{ model: Vendor, as: 'vendor' },
{ model: ProductImages, as: 'product_img_refs' }
]
});
return prodData;
}
I am not getting the correct way to bind the all models that will return me a result as described in the above json format.
To get the nested output as shown in the question, you would need to create associations between the following:
ProductImages and ImagesModel
ProductImages and ImageTypes
Once done, you can nest the models in the findAll options as shown below:
// Create associations (depends on your data, may be different)
ProductImages.hasOne(ImagesModel);
ProductImages.hasOne(ImageTypes);
// Return product data with nested models
let prodData = Product.findAll({
include: [
{ model: Vendor, as: 'vendor' },
{
model: ProductImages, as: 'product_img_refs',
include: [
{ model: ImagesModel }, // Join ImagesModel onto ProductImages (add aliases as needed)
{ model: ImageTypes } // Join ImageTypes onto ProductImages (add aliases as needed)
]
}
]
});
I found the issue. you were trying to include ProductImages model into Vendor. As per your association, ProductImages associate with Product not with Vendor.
So please try this
let prodData = Product.findAll({
include: [
{ model: Vendor, as: 'vendor' },
{ model: ProductImages }
]
});

Sequelize User M2M Group - Find Users that are in Groups

In my project, I have Users and Groups, and a User can have multiple Groups. I'm having trouble filtering a list of users based on those who are in a certain group.
Here's my setup:
User Model
module.exports = (sequelize) => {
const model = sequelize.define('users',
{
id: {
type: Sequelize.INTEGER,
autoIncrement: true,
primaryKey: true,
field: 'uID',
},
username: {
type: Sequelize.STRING, allowNull: false, unique: true, field: 'uEmail',
},
}, {
timestamps: false,
tableName: 'Users',
});
model.modelName = 'users';
return model;
};
Group Model
module.exports = (sequelize) => {
const model = sequelize.define('groups', {
id: {
type: Sequelize.INTEGER,
autoIncrement: true,
primaryKey: true,
field: 'gID',
},
name: { type: Sequelize.STRING, allowNull: false, field: 'gName' },
description: { type: Sequelize.STRING, allowNull: false, field: 'gDescription' },
}, {
timestamps: false,
tableName: 'Groups',
});
model.modelName = 'groups';
return model;
};
M2M Through Table :: UserGroups
module.exports = (sequelize) => {
const model = sequelize.define('usergroups', {
}, {
timestamps: false,
tableName: 'UserGroups',
});
model.modelName = 'usergroups';
return model;
};
Association
User.belongsToMany(Group, {
through: UserGroup,
as: 'groups',
foreignKey: 'uID',
});
Group.belongsToMany(User, {
through: UserGroup,
as: 'users',
foreignKey: 'gID',
});
My db Users look like this (in shorthand):
User1.groups = [1,2,3]
User2.groups = [1,3]
User3.groups = [2,3]
I'm building the search rest API: GET /users?groups[]=1&groups[]=2
In building the query, I've tried to filter based on the groups passed in using this format:
let query = {};
query.include = [
{
model: Group,
as: 'groups',
where: {id: {[Op.in]: filter.groups}},
attributes: ['id', 'name'],
},
];
const result = yield User.findAndCountAll(query);
or tried using through (with required: true or required: false):
let query = {};
query.include = [
{
model: Group,
as: 'groups',
through: { where: {'gID': {[Op.in]: filter.groups}}, required: true},
attributes: ['id', 'name'],
},
];
const result = yield User.findAndCountAll(query);
It kinda works, except the returned objects associated groups are abbreviated. If I pass in this filter:
GET /users?groups[]=1
The response comes back with the right users, but the groups associated with those users are filtered:
[
{
"id": 1,
"username": "user1#site.com",
"groups": [
{
"id": 1,
"name": "My Group",
"usergroups": {
"uID": 1,
"gID": 1
}
}
]
},
{
"id": 2,
"username": "user2#site.com",
"groups": [
{
"id": 1,
"name": "My Group",
"usergroups": {
"uID": 2,
"gID": 1
}
}
]
}
]
But where are groups 2 and 3 for User1, and group 2 for User2?
It's almost like I need to get all the user ids in a specific group (through the Group service class) then do:
query.where = {id: {[Op.in]: userIdsArray}}

Resources