Sequelize Many to Many relationship issues - sql

I'm trying to build a simple n:m relationship, the case is, one Doctor can have many Patients and a Patient can have many Doctors...
Doctor Model
module.exports = (sequelize, DataTypes) => {
const Doctor = sequelize.define('Doctor', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
doc_crm: {
type: DataTypes.STRING,
allowNull: false
}
}, {
tableName: 'doctors',
timestamps: false
})
Doctor.associate = function (models) {
Doctor.belongsToMany(models.Patient, { as: 'Patients', through: { model: models.PatientDoctor } })
Doctor.belongsTo(models.User, { foreignKey: 'user_id' })
}
return Doctor
}
Patient Model
module.exports = (sequelize, DataTypes) => {
const Patient = sequelize.define('Patient', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
}
}, {
tableName: 'patients',
timestamps: false
})
Patient.associate = function (models) {
Patient.belongsToMany(models.Doctor, { as: 'Doctors', through: { model: models.PatientDoctor } })
Patient.belongsTo(models.User, { foreignKey: 'user_id' })
}
return Patient
}
And the joining table is
PatientDoctor
module.exports = (sequelize, DataTypes) => {
const PatientDoctor = sequelize.define('PatientDoctor', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
}
}, {
tableName: 'patient_doctors',
timestamps: false
})
return PatientDoctor
}
So, when i query the PatientDoctor model passing the Patient and Doctor i get a
{
"name": "SequelizeEagerLoadingError"
}
What i'm doing wrong? i tried a lot of stuff, all without luck.
Thanks in advance!

Related

I have a trouble with association with sequelize and postgres

I have two tables defined, one for movies, one for characters, these are interconnected, by the MovieCharacter table.
Setting the tables to maintain a one-to-many relationship via belongstomany allows me to create a duplicate relationship, and I'm not getting around it.
I leave my code below, i have some experience with mongoose and nosql db but this is new for me.
Thanks!
charModel.js
const { Model, DataTypes } = require('sequelize');
const CHARACTER_TABLE = 'character';
const CharacterSchema = {
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
allowNull: false,
},
name: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
},
age: {
type: DataTypes.BIGINT,
allowNull: false,
},
weight: {
type: DataTypes.BIGINT,
allowNull: false,
},
history: {
type: DataTypes.STRING,
allowNull: false
},
image: {
type: DataTypes.STRING,
allowNull: false
},
};
modelMovie.js
class Character extends Model {
static associate(models){
this.belongsToMany(models.Movie, {
as: "movies",
through: "MovieCharacter",
foreignKey: "characterId",
otherKey: "movieId",
});
}
static config(sequelize){
return {
sequelize,
tableName: CHARACTER_TABLE,
modelName: 'Character',
timestamps: false,
}
}
}
const moment = require("moment");
const { Model, DataTypes } = require("sequelize");
const { GENRES_TABLE } = require("./genre.model");
const MOVIES_TABLE = "movie";
const MovieSchema = {
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
allowNull: false,
},
title: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
},
creationDate: {
field: "creation_date",
type: DataTypes.STRING,
get(){
return moment(this.getDataValue('creationDate')).format('DD-MM-YYYY')
},
allowNull: false,
},
rating: {
type: DataTypes.INTEGER,
allowNull: false,
},
image: {
type: DataTypes.STRING,
allowNull: false,
},
type: {
type: DataTypes.STRING,
allowNull: false,
},
genreId: {
field: "genre_id",
type: DataTypes.INTEGER,
allowNull: false,
references: {
model: GENRES_TABLE,
key: "id",
},
onUpdate: "CASCADE",
onDelete: "SET NULL",
},
};
class Movie extends Model {
static associate(models) {
this.belongsToMany(models.Character, {
as: "characters",
through: "MovieCharacter",
foreignKey: "movieId",
otherKey: "characterId",
});
this.belongsTo(models.Genre, {
as: "genre",
});
}
static config(sequelize) {
return {
sequelize,
tableName: MOVIES_TABLE,
modelName: "Movie",
timestamps: false,
};
}
}
module.exports = { MOVIES_TABLE, MovieSchema, Movie };
modelCharMov.js
const { Model, DataTypes} = require('sequelize');
const { CHARACTER_TABLE} = require('./character.model');
const { MOVIES_TABLE} = require('./movies.model')
const MOVIES_CHARACTERS_TABLE = 'movies_characters';
const MoviesCharactersSchema = {
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
allowNull: false,
},
movieId:{
field: 'movie_id',
type: DataTypes.INTEGER,
allowNull: false,
references: {
model: MOVIES_TABLE,
key: 'id',
},
onUpdate: 'CASCADE',
onDelete: 'SET NULL'
},
characterId:{
field: 'character_id',
type: DataTypes.INTEGER,
allowNull: false,
references: {
model: CHARACTER_TABLE,
key: 'id'
},
onUpdate: 'CASCADE',
onDelete: 'SET NULL'
}
};
class MovieCharacter extends Model {
static config(sequelize) {
return {
sequelize,
tableName: MOVIES_CHARACTERS_TABLE,
modelName: 'MovieCharacter',
timestamps: false
};
}
}
Add 2 BelongsTo associations on the junction table pointing to the tables that are using it to find one another. That would be my first guess because that is the only thing that would have been different in my code compared to yours if implementing this. If that doesn’t work, more details please sir 🧐

I tried to add self reference relationship using Sequelize

There is a table called employees and the employee id wants to self reference to the employee table when the employee has a team lead.
Team lead is also an employee. So how to self reference using Sequelize?
const Employee = sequelize.define("employee", {
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
allowNull: false,
},
first_name: {
type: DataTypes.STRING(45),
allowNull: false,
},
lead_role: {
type: DataTypes.BOOLEAN,
allowNull: false,
defaultValue: false,
},
designation: {
type: DataTypes.STRING(100),
allowNull: true,
},
resignation_date: {
type: DataTypes.DATE,
allowNull: true,
},
lead_id: {
type: DataTypes.INTEGER,
allowNull: true,
},
});
this is my employee model associations
Employee.associate = function (models) {
Employee.hasOne(models.user, { foreignKey: "employee_id" });
Employee.hasMany(models.leave_request, {
foreignKey: "employee_id",
});
Employee.belongsTo(models.team, {
foreignKey: "team_id",
});
Employee.belongsTo(models.team_manager, {
foreignKey: "manager_id",
});
Employee.hasMany(models.attendance, {
foreignKey: "employee_id",
});
Employee.belongsTo(models.employee, {
foreignKey: "lead_id",
});
Employee.hasMany(models.employee, {
foreignKey: "lead_id",
});
};
You just need to indicate aliases for both a team lead link and employees link:
Employee.belongsTo(models.employee, {
foreignKey: "lead_id",
as: 'TeamLead'
});
Employee.hasMany(models.employee, {
foreignKey: "lead_id",
as: 'TeamEmployees'
});
And you need to indicate the same aliases in queries:
const employees = await Employee.findAll({
where: {
team_id: teamId
},
include: [{
model: Employee,
as: 'TeamLead'
}]
})
const teamLead = await Employee.findAll({
where: {
id: teamLeadId
},
include: [{
model: Employee,
as: 'TeamLead'
}]
})

Sequelize Count and Group Nested Include

I have got a few sql tables as
export default (sequelize, Sequelize) => {
return sequelize.define('teacher', {
tagline: {
type: Sequelize.TEXT,
},
modeOfPayment: {
type: Sequelize.STRING,
},
modeOfSession: {
type: Sequelize.STRING,
},
preferredTimeZones: {
type: Sequelize.STRING,
},
titleForSessions: {
type: Sequelize.STRING,
},
availableForWork: {
type: Sequelize.BOOLEAN,
},
});
};
export default (sequelize, Sequelize) => {
return sequelize.define('skill', {
name: {
type: Sequelize.STRING,
unique: true,
allowNull: false,
},
});
};
export default (sequelize, Sequelize) => {
return sequelize.define('category', {
name: {
type: Sequelize.STRING,
unique: true,
allowNull: false,
},
});
};
Here is the model relation between them
Teacher.belongsToMany(Skill, {
through: 'skill_teacher',
});
Skill.belongsToMany(Teacher, {
through: 'skill_teacher',
});
Category.hasMany(Skill);
Skill.belongsTo(Category);
Earlier I needed to query count of teachers in each skill, here's my controller for that
// #desc Get skill count for each skill
// #route GET /api/skills/count
// #access Public
const getSkillCount = asyncHandler(async (req, res) => {
try {
const skills = await Skill.findAll({
attributes: [
'id',
'name',
[sequelize.fn('count', sequelize.col('teachers.id')), 'teacherCount'],
],
include: [{ attributes: [], model: Teacher }],
group: ['skill.id'],
});
res.json(skills);
} catch (err) {
console.log(err.message);
res.status(500);
throw new Error(err.message);
}
});
Now, the skills are grouped in categories. So I wanna query a list of skills having their own teacher count grouped in their own categories, which category also having a skillCount column. I tried this but it is not giving my desired results
// #desc Get category and their skills counts
// #route GET /api/categories/skills/count
// #access Public
const getCategorySkillCounts = asyncHandler(async (req, res) => {
try {
const categories = await Category.findAll({
attributes: [
'id',
'name',
[sequelize.fn('count', sequelize.col('skills.id')), 'skillCount'],
],
include: {
model: Skill,
include: [{ model: Teacher }],
attributes: [
'id',
'name',
[sequelize.fn('count', sequelize.col('teachers.id')), 'teacherCount'],
],
group: ['skill.id'],
},
group: ['category.id'],
});
res.json(categories);
} catch (err) {
console.log(err.message);
res.status(500);
throw new Error(err.message);
}
});

Sequelize select with two relations to the same entity

I have 2 tables, ItemLegacy :
module.exports = function(sequelize, DataTypes) {
return sequelize.define('ItemLegacy', {
id: {
type: DataTypes.INTEGER(11).UNSIGNED,
allowNull: false,
primaryKey: true
},
parent: {
type: DataTypes.INTEGER(11),
allowNull: false,
defaultValue: 0,
},
child: {
type: DataTypes.INTEGER(11),
allowNull: false,
defaultValue: 0
}
}, {
tableName: 'ItemLegacy',
timestamps: false,
underscored: false
});
};
and Item :
module.exports = function(sequelize, DataTypes) {
return sequelize.define('Item', {
id: {
type: DataTypes.INTEGER(11).UNSIGNED,
allowNull: false,
primaryKey: true
},
title: {
type: DataTypes.STRING(500),
allowNull: false,
defaultValue: ''
},
code: {
type: DataTypes.STRING(20),
allowNull: true
},
}, {
tableName: 'Item',
timestamps: false,
underscored: false
});
};
I also defined two relationships :
db.ccnLegacy.hasOne(db.ccn, { foreignKey: 'id', sourceKey: 'parent' })
db.ccnLegacy.hasOne(db.ccn, { foreignKey: 'id', sourceKey: 'child' })
My question is, I would like to create a select request using sequelize, with a relation for each of the 2 fields parent and child.
I know how to do that with one relation, but how do I do it with 2 ?
My code with only one relation :
db.itemLegacy.findOne({
raw: true,
where: { child: idChild },
include: [
{
model: db.item
},
]
})
You simply should indicate aliases for both associations and use them in queries. Second is you used hasOne instead of belongTo because belongTo is used exactly in a case when you go from N to 1 in N:1 relationship.
Associations:
db.ccnLegacy.belongsTo(db.ccn, { foreignKey: 'parent', as: 'Parent' })
db.ccnLegacy.belongsTo(db.ccn, { foreignKey: 'child', as: 'Child' })
Query:
db.itemLegacy.findOne({
raw: true,
where: { child: idChild },
include: [
{
model: db.item,
as: 'Child'
},
{
model: db.item,
as: 'Parent'
},
]
})

Sequelize Association Error Not Associated

I am trying to associate my User model with my Organization model, but I'm running into an error that says, Error: user is not associated to organization! despite the fact that I am following the process to associate the User to my Organization. Is it possible that the type of association method that I am using is causing the problem?
User Model (user.js):
var bcrypt = require('bcrypt-nodejs');
module.exports = function(sequelize, DataTypes) {
var User = sequelize.define('user', {
user_id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true
},
firstName: {
type: DataTypes.STRING,
field: 'first_name'
},
lastName: {
type: DataTypes.STRING,
field: 'last_name'
},
email: {
type: DataTypes.STRING,
isEmail: true,
unique: true
},
password: DataTypes.STRING,
organizationId: {
type: DataTypes.INTEGER,
field: 'organization_id',
allowNull: true
}
}, {
freezeTableName: true,
classMethods: {
generateHash: function(password) {
return bcrypt.hashSync(password, bcrypt.genSaltSync(8), null);
},
associate: function(db) {
User.belongsTo(db.Organization, {foreignKey: 'organizationId'});
},
},
instanceMethods: {
validPassword: function(password) {
return bcrypt.compareSync(password, this.password);
},
},
});
return User;
}
Organization model (organization.js):
module.exports = function(sequelize, DataTypes) {
var Organization = sequelize.define('organization', {
organizationId: {
type: DataTypes.INTEGER,
field: 'organization_id',
autoIncrement: true,
primaryKey: true
},
organizationName: {
type: DataTypes.STRING,
field: 'organization_name'
},
admin: DataTypes.STRING,
members: DataTypes.STRING
},{
freezeTableName: true
});
return Organization;
}
index for tables to connect (db-index.js):
var Sequelize = require('sequelize');
var path = require('path');
var config = require(path.resolve(__dirname, '..', '..','./config/config.js'));
var sequelize = new Sequelize(config.database, config.username, config.password, {
host:'localhost',
port:'3306',
dialect: 'mysql'
});
sequelize.authenticate().then(function(err) {
if (!!err) {
console.log('Unable to connect to the database:', err)
} else {
console.log('Connection has been established successfully.')
}
});
var db = {}
db.Organization = sequelize.import(__dirname + "/organization");
db.User = sequelize.import(__dirname + "/user");
db.Records = sequelize.import(__dirname + "/records");
db.User.associate(db);
db.Records.associate(db);
db.sequelize = sequelize;
db.Sequelize = Sequelize;
sequelize.sync();
module.exports = db;
Are of a route call that triggers this error:
appRoutes.route('/sign-up/organization')
.get(function(req, res){
models.User.find({
where: {
user_id: req.user.email
}, attributes: [ 'user_id', 'email'
]
}).then(function(user){
res.render('pages/sign-up-organization.hbs',{
user: req.user
});
})
})
.post(function(req, res, user){
models.Organization.create({
organizationName: req.body.organizationName,
admin: req.body.admin,
user: {
organizationId: req.body.organizationId
}
}, { include: [models.User] }).then(function(){
console.log(user.user_id);
res.redirect('/app');
}).catch(function(error){
res.send(error);
console.log('Error at Post');
})
});
You need to set up the reverse of the association, Organization hasMany Users