Many to Many Relationship Sequelize Node JS Array of Attributes - node.js

I have a Job Model
module.exports = (sequelize, Datatypes) => {
const Jobs = sequelize.define('Jobs', {
jobinput: {
type: Datatypes.STRING
},
typeinput: {
type: Datatypes.STRING
},
jobdescinput: {
type: Datatypes.TEXT('medium')
},
locationinput: {
type: Datatypes.STRING
},
salarymininput: {
type: Datatypes.INTEGER
},
salarymaxinput: {
type: Datatypes.INTEGER
},
seniorityinput: {
type: Datatypes.STRING
},
vacancyinput: {
type: Datatypes.INTEGER
},
bountyinput: {
type: Datatypes.INTEGER
}
})
Jobs.associate = function (models) {
Jobs.belongsTo(models.User)
Jobs.belongsTo(models.Company)
Jobs.belongsToMany(models.JobSkills, { through: models.JobHasSkills, foreignKey: 'JobId' })
}
return Jobs
}
and a JobSkill Model
module.exports = (sequelize, Datatypes) => {
const JobSkills = sequelize.define('JobSkills', {
skill: {
type: Datatypes.STRING,
unique: true
}
})
JobSkills.associate = function (models) {
JobSkills.belongsToMany(models.Jobs, { through: models.JobHasSkills, foreignKey: 'JobSkillId' })
}
return JobSkills
}
and a Many to Many JobHasSkills Model
module.exports = (sequelize, Datatypes) => {
const JobHasSkills = sequelize.define('JobHasSkills', { })
JobHasSkills.associate = function (models) {
JobHasSkills.belongsTo(models.Jobs)
JobHasSkills.belongsTo(models.JobSkills)
}
return JobHasSkills
}
and I was trying to do a create function
async create (req, res) {
try {
await Jobs.create(req.body, { include: { model: JobSkills } })
.then(function (createdObjects) {
res.json(createdObjects)
})
} catch (err) {
res.status(500).send({
error: 'an error has occured while posting'
})
}
}
My Json data that i sent through postman is
{ "jobinput": "Title",
"typeinput": "Part Time",
"jobdescinput": "lorem ipsum",
"locationinput": "California",
"salarymininput": "0",
"salarymaxinput": "1000",
"seniorityinput": "Internship",
"vacancyinput": "1",
"bountyinput": "10",
"skills": ["skill1","skill2","skill3" ]
}
I checked this answer Trying to create an instance and multiple related instances in a many to many relationship
But I was getting the catch(error).
This is my first project with node js and sequelize. Can anyone help me on this

If your tables are not getting created than please use -
sequelize.define(
"Table Name"
{
Model Definations
},
{
underscored: false,// Not Required in your case
timestamps: false// Not Required in your case
}
).sync({ force: true });
Once tables are created do comment the Sync option otherwise on every restart tables will be dropped and recreated.

Related

Sequelize association table group

I have two tables medforms and category each medform can have multiple categories. for multiple categories, I made another association table medform_categories.
'use strict';
const {
Model
} = require('sequelize');
module.exports = (sequelize, DataTypes) => {
class MedForm extends Model {
/**
* Helper method for defining associations.
* This method is not a part of Sequelize lifecycle.
* The `models/index` file will call this method automatically.
*/
static associate(models) {
// define association here
}
};
MedForm.init({
med_id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
med_name: {
type: DataTypes.STRING,
allowNull: false
},
}, {
sequelize,
modelName: 'medforms',
});
MedForm.associate = function (models) {
MedForm.hasMany(models.medforms_category, {
foreignKey: 'med_id',
as: 'medform'
});
};
return MedForm;
};
category table schema:
'use strict';
const {
Model
} = require('sequelize');
module.exports = (sequelize, DataTypes) => {
class Category extends Model {
static associate(models) {
}
};
Category.init({
category_id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: DataTypes.INTEGER
},
category_name: {
type: DataTypes.STRING,
},
}, {
sequelize,
modelName: 'categories',
});
Category.associate = function (models) {
Category.hasMany(models.medforms_category, {
foreignKey: 'category_id',
as: 'category'
});
};
return Category;
};
and finally the association table medform_category
'use strict';
const {
Model
} = require('sequelize');
module.exports = (sequelize, DataTypes) => {
class Medform_categories extends Model {
static associate(models) {
}
};
Medform_categories.init({
medform_category_id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
category_id: {
type: DataTypes.INTEGER,
references: {
model: 'categories',
key: 'category_id'
},
onUpdate: 'CASCADE',
onDelete: 'CASCADE'
},
med_id: {
type: DataTypes.INTEGER,
allowNull: false,
references: {
model: 'medforms',
key: 'med_id'
},
onUpdate: 'CASCADE',
onDelete: 'CASCADE'
},
}, {
sequelize,
modelName: 'medform_categories',
});
Medform_categories.associate = function (models) {
Medform_categories.belongsTo(models.medforms, {
foreignKey: 'med_id',
as: 'medform'
});
Medform_categories.belongsTo(models.categories, {
foreignKey: 'category_id',
as: 'category'
});
};
return Medform_categories;
};
now I want data like:
"category": [
{
"category_id": 1,
"category_name": "cat1",
"medforms": {
"med_id": 1,
"med_name": "Mobilfunk 1",
"med_fullname": "Mobilfunk 1",
},
{
"med_id": 2,
"med_name": "Mobilfunk 2",
"med_fullname": "Mobilfunk 2",
}
},
{
"category_id": 2,
"category_name": "cat2",
"medforms": {
"med_id": 1,
"med_name": "Mobilfunk 1",
"med_fullname": "Mobilfunk 1",
},
{
"med_id": 3,
"med_name": "Mobilfunk 3",
"med_fullname": "Mobilfunk 3",
}
},
]
can some one please help me here? what I should do here? Inside the
controller to fetch data like mentioned above?
const db = require("../models");
const Categories = db.categories; // tables db instance
// Retrieve all objects (with include)
exports.getAllCategories = (req, res) => {
Categories.findAll({
include: [
{
model: db.medforms_category,
as: 'category',
include: [
{
model: db.medforms,
as: 'medform',
}]
},
],
})
.then(data => {
res.send(data);
})
.catch(err => {
res.status(500).send({
message:
err.message || "Some error occurred while retrieving all data."
});
});
};
If you have a N:N relation, you not necessarely need to have a Model to represent this table.
// Medforms
// ...
static associate(models) {
this.belongsToMany(models.categories, { through: 'medform_categories' });
}
// Categories
// ...
static associate(models) {
this.belongsToMany(models.medforms, { through: 'medform_categories' });
}
// Query will be something like
const categories = await Categories.findAll({
where: {...},
include: ['medforms']
})
const medforms = await Medforms.findAll({
where: {...},
include: ['categories']
})

Sequelize (PostgresSQL) many to many chat implementation

I've been trying to create a chat app with Node JS and Sequelize. Now i'm stuck at a problem of creating a query to find a conversation that has my id and user's id(the one i'm trying to text). So the thing i'm trying to do is send a post request with and id of a user i'm sending a message to, then i look through my Conversation model and check if that conversation has my id and id of the user i'm texting to.
My models are associated through Many to Many relationship. So the main objective is to find a conversation with only my ID and ID of the user i'm texting to with the same ConversationId.
Here are my models:
User
module.exports = (sequelize, DataTypes) => {
const User = sequelize.define(
"User",
{
name: { type: DataTypes.STRING },
password: { type: DataTypes.STRING, allowNull: false },
username: { type: DataTypes.STRING, allowNull: false },
email: { type: DataTypes.STRING, allowNull: false },
},
{}
);
User.belongsToMany(models.Conversation, {
as: "conversations",
foreignKey: "user_id",
through: models.ConversationUsers,
});
User.hasMany(models.Message, {
as: "messages",
});
};
return User;
};
Conversation
module.exports = (sequelize, DataTypes) => {
const Conversation = sequelize.define(
"Conversation",
{
lastMessage: DataTypes.STRING,
recipients: DataTypes.ARRAY(DataTypes.INTEGER),
},
{
sequelize,
modelName: "Conversation",
}
);
Conversation.associate = (models) => {
Conversation.belongsToMany(models.User, {
as: "participants",
foreignKey: "conversation_id",
through: models.ConversationUsers,
});
Conversation.hasMany(models.Message, {
as: "messages",
});
};
return Conversation;
};
ConversationUsers Many to Many through model
"use strict";
module.exports = (sequelize, DataTypes) => {
const ConversationUsers = sequelize.define(
"ConversationUsers",
{
user_id: DataTypes.INTEGER,
conversation_id: DataTypes.INTEGER,
},
{
sequelize,
modelName: "ConversationUsers",
}
);
return ConversationUsers;
};
Message
module.exports = (sequelize, DataTypes) => {
const Message = sequelize.define(
"Message",
{
conversationId: { type: DataTypes.INTEGER, allowNull: false },
sentTo: DataTypes.INTEGER,
sentFrom: DataTypes.INTEGER,
body: { type: DataTypes.STRING, allowNull: false },
},
{
sequelize,
modelName: "Message",
}
);
Message.associate = (models) => {
Message.belongsTo(models.User, {
as: "messageTo",
foreignKey: "sentTo",
});
Message.belongsTo(models.User, {
as: "messageFrom",
foreignKey: "sentFrom",
});
Message.belongsTo(models.Conversation, {
as: "messages",
});
};
return Message;
};
I think you can remove some pieces from your models and rework it a bit.
Messages don't need a sentTo, they only need a sentFrom. You can use the ConversationUsers table to know who the recipients are. This also gives you the flexibility to have Conversations with more than 2 members, because your current model essentially enforces that a Message can only be to one user.
So let's walk through the models first with changes
module.exports = (sequelize, DataTypes) => {
const User = sequelize.define(
"User",
{
name: { type: DataTypes.STRING },
password: { type: DataTypes.STRING, allowNull: false },
username: { type: DataTypes.STRING, allowNull: false },
email: { type: DataTypes.STRING, allowNull: false },
},
{
// I think moving the associations to other files might make this more clear
}
);
};
return User;
};
module.exports = (sequelize, DataTypes) => {
const Conversation = sequelize.define(
"Conversation",
{
// perhaps something like a subject could go here e.g.
subject: DataTypes.STRING(500),
},
{
sequelize,
modelName: "Conversation",
}
);
Conversation.associate = (models) => {
Conversation.hasMany(models.Message, {
as: "ConversationMessages",
}); // adds ConversationId onto Message, gives us Conversation.getConversationMessages() etc
models.Message.belongsTo(Conversation); // create association both ways for convenience methods to find convo from a message
models.Message.hasOne(Conversation, {
as: 'LastMessage',
constraints: false,
allowNull:true,
defaultValue:null
}); // adds LastMessageId onto Conversation model (you'll have to write code to maintain this value, probably through an afterCreate hook on Message model)
};
return Conversation;
};
module.exports = (sequelize, DataTypes) => {
const Message = sequelize.define(
"Message",
{
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true, // if you want to do the hook thing i talked about to set LastMessageId, you need to put this in
},
body: { type: DataTypes.STRING, allowNull: false },
},
{
sequelize,
modelName: "Message",
}
);
Message.associate = (models) => {
Message.belongsTo(models.User, {as: "sentFromUser"});
};
return Message;
};
// I'm going to rename your many-to-many table "ConversationMembers"
module.exports = (sequelize, DataTypes) => {
const ConversationMembers = sequelize.define(
"ConversationMembers",
{
// again, the associations will build these fields for you
},
{
sequelize,
modelName: "ConversationMembers",
}
);
models.Conversation.belongsToMany(models.User, {
through: "ConversationMember",
as: "Members",
}); // gives us Conversation.getMembers()
models.User.belongsToMany(models.Conversation, {
through: "ConversationMember",
as: "MemberConversations",
}); // gives us User.getMemberConversations()
ConversationMember.belongsTo(models.Message, { as: "LastReadMessage" }); // gives us the potential ability to track the last read message for each convo member as ConversationMember.LastReadMessageId, you'll need to set this value manually on read for each user if you care about having it
models.Conversation.hasMany(ConversationMember);
models.User.hasMany(ConversationMember);
return ConversationMember;
Okay now onto your question, which perhaps becomes simpler at this point. If you already know the ConversationId, all you need to do is check that the person who is sending the message is a member of the conversation. Then if they are, write a row into the Messages table. It doesn't matter who the message is "to"--you're writing to the members of the Conversation, not to any individual.
async function canMessageHelper({conversationId, userId }) {
const convo = await models.Conversation.findOne({
attributes: ["id"], // whatever attrs you need, probably not many if any
where: {
id: conversationId,
},
include: [{
model: models.ConversationMember,
attributes: ["ConversationId"], // whatever you need if anything
where: { // this where is critical, it creates an inner join so convo only returns if myUserId is a member of the Conversation
UserId: userId
}
}]
});
if (!convo) {
return false;
}
return convo;
}
async function sendMessage({conversationId, authorUserId, messageText}) {
const allowMessage = await canMessageHelper({conversationId, userId: authorUserId});
if (!allowMessage) {
return false;
}
await models.Message.create({sentFromUserId: authorUserId, body: messageText});
}
If you want to try this, be sure you remove any tables you've already created with these names from your database before you sync.
I have not provided any code for the hooks I mentioned, but you will have the groundwork to develop those ideas out.

How do I establish a one-to-many association between two models in sequelize?

I have made two sequelize models, one for storing students and one for storing their tasks.
I want to establish a one to many relation between student and task model...
The task model
const Student= require('./student.js')
module.exports = (sequelize, Sequelize) => {
const Task = sequelize.define("task", {
title: {
type: Sequelize.STRING
},
description: {
type: Sequelize.STRING
}
});
Task.associate = (Student) => {
Task.belongsTo(Student)
};
return Task;
};
The Student model
const Task = require('./task.js')
module.exports = (sequelize, Sequelize) => {
const Student = sequelize.define("student", {
name: {
type: Sequelize.STRING,
allowNull:false
},
email: {
type: Sequelize.STRING,
unique:true,
allowNull:false
},
branch: {
type: Sequelize.STRING,
allowNull:false
},
id: {
type: Sequelize.INTEGER,
autoIncrement: true,
primaryKey: true
},
});
Student.associate = (Task) =>{
Student.hasMany(Tasks, {as: "tasks"))
}
return Student;
};
I want to fetch the tasks of each student along with the student data..
below is the function for same
// Retrieve all Students from the database.
const Task= require('../models/task.js')
exports.findAll = (req, res) => {
Student.findAll({
include: Task
})
.then(data => {
res.send(data);
})
.catch(err => {
res.status(500).send({
message:
err.message || "Some error occurred while retrieving students."
});
});
};
But when I hit the query, I get the following error.
{
"message": "task is not associated to student!"
}
I'm stuck at this issue for 2 days...kindly help me sort this out.
Thank you in advance

How to build model with sequelize for belong to many association

This is what I wrote in Country.js (exactly the same as User.js except datatypes) :
module.exports = function(sequelize, DataTypes) {
const Country = sequelize.define('country',
{
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
code: {
type: DataTypes.INTEGER
},
alpha2: {
type: DataTypes.STRING
},
alpha3: {
type: DataTypes.STRING
},
name_en: {
type: DataTypes.STRING
},
name_fr: {
type: DataTypes.STRING
}
},
{
freezeTableName: true,
timestamps: false
});
Country.associate = ( models ) => {
models.Country.belongsToMany(models.User, {
through: 'country_user',
as: 'user',
foreignKey: 'id_country'
});
};
return Country;
}
This is my query :
router.get('/thisuserCountries', function(req, res, next){
User(db, Sequelize.DataTypes).findOne({
include: [{
model: Country(db, Sequelize.DataTypes),
as: 'countries',
required: false,
attributes: ['id'],
}],
where: {
email: 'jerome.charlat#gmail.com'
}
})
.then(user => {
if(user) {
res.json(user)
}
else {
res.send('User does not exist')
}
})
.catch(err => {
res.send('error: ' + err)
})
})
This is my db.js :
const Sequelize = require('sequelize')
const db = new Sequelize('travel_memories', 'root', '', {
host: 'localhost',
dialect: 'mysql',
port: 3306
})
db
.authenticate()
.then(() => {
console.log('Connection has been established successfully.');
})
.catch(err => {
console.error('Unable to connect to the database:', err);
});
const models = {
Country: db.import('../models/Country'),
User: db.import('../models/User'),
CountryUserJoin: db.import('../models/Country_user')
};
Object.keys(models).forEach((modelName) => {
if('associate' in models[modelName]){
models[modelName].associate(models);
}
});
module.exports = db
Postman says : error SequelizeEagerLoadingError: country is not associated to user!
But, I think I should write in the through parameter the model User_country when I associate tables in each model. So i tried to write something like :
Country.associate = ( models ) => {
models.Country.belongsToMany(models.User, {
through: models.Country_user,
as: 'user',
foreignKey: 'id_country'
});
};
And console says when I launch server, before querying anything :
SequelizeAssociationError: country.belongsToMany(user) requires through option, pass either a string or a model.
So I am blocked. I used the example in documentation to write the assocation with models.foo. But in fact models comes from nowhere..
Thanks again for your help !
There's not a lot of documentation about this, but here it says that you should use a through option when querying or selecting belongs-to-many attributes, just like this:
...
User(db, Sequelize.DataTypes).findOne({
include: [{
model: Country(db, Sequelize.DataTypes),
as: 'countries',
required: false,
through: {
attributes: ['id']
}
}],
where: {
email: 'jerome.charlat#gmail.com'
}
})
...

Update with association using Sequelize.js

I am trying to update with association using sequelize.js.
I have tried give example on stackoverflow namely the following links:
Sequelize update with association
Sequelize update with association
Updating attributes in associated models using Sequelize
all of these links did not get me to the goal i am trying to accomplish.
My model is as follow, I have a country module and a city module. a country has many cities. please refer to the module bellow.
Please advise.
country.js file
module.exports = function (sequelize, DataTypes) {
var country= sequelize.define('COUNTRY', {
COUNTRY_ID: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
COUNTRY_NAME: DataTypes.STRING,
COUNTRY_CURRENCY: DataTypes.STRING
}, {
freezeTableName: true,
classMethods: {
associate: function (models) {
COUNTRY_ID.hasMany(models.CITIES, {
foreignKey: 'COUNTRY_ID'
})
}
},
instanceMethods: {
updateAssociation: function (onSuccess, onError) {
country.findAll({
where: {
COUNTRY_ID: req.params.country_id
},
include: [
{
model: sequelize.import('./cities.js'),
}
]
})
})
.then(country =>{
const updatePromises = country.map(countries =>{
return countries.updateAttributes(req.body);
});
const updatePromisescities = list.CITY.map(cities =>{
return cities.updateAttributes(req.body.CITYs[0]);
});
return sequelize.Promise.all([updatePromises, updatePromisescities ])
}).then(onSuccess).error(onError);
}
}
});
return country;
};
city.js file
module.exports = function (sequelize, DataTypes) {
var CITY = sequelize.define('LIST_CODE', {
CITY_ID: {
type: DataTypes.INTEGER,
primaryKey: true
},
COUNTRY_ID: {
type: DataTypes.INTEGER,
primaryKey: true
}
}, {
freezeTableName: true,
timestamps: false,
classMethods: {
associate: function (models) {
// associations can be defined here
CITY.belongsTo(models.COUNTRY, {
foreignKey: 'COUNTRY_ID'
})
}
}
});
return CITY;
};

Resources