Sequelize association table group - node.js

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']
})

Related

Sequelizenot mapping correctly belongs to many

I have a Categories table, Attributes and an intermediate table, AttributeCategory, which has id, attribute_id, category_id, date and value, the problem is that when sequelize maps the relationship if the attribute_id and category_id match it does not map it correctly, example:
Category table: id = 1, description = "some"
Attribute table: id = 1, description = "one", id = 2, description = "two"
Category Attribute Table,
id = 1, category_id = 1, attribute_id = 1, value = 10, date = 2020-05-10
id = 2, category_id = 1, attribute_id = 1, value = 15, date = 2020-05-15
id = 3, category_id = 1, attribute_id = 2, value = 10, date = 2020-05-10
The query to db does it correctly, but when it maps the object it only returns me:
id = 2, category_id = 1, attribute_id = 1, value = 15, date = 2020-05-15
id = 3, category_id = 1, attribute_id = 2, value = 10, date = 2020-05-10
as the same attribute_id and category_id are repeated, it only returns the last one.
My models are:
// Atrributes
'use strict';
const { Model } = require('sequelize');
module.exports = (sequelize, DataTypes) => {
class InmAtributo 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) {
// Atributo tiene varias categorias
InmAtributo.belongsToMany(models.InmCategoria, {
as: 'categorias',
through: models.InmAtributoCategoria,
foreignKey: 'atributo_id',
otherKey: 'categoria_id'
});
}
};
InmAtributo.init({
descripcion: DataTypes.STRING,
abreviado: DataTypes.STRING
}, {
sequelize,
modelName: 'InmAtributo',
tableName: 'inm_atributos',
timestamps: false,
});
return InmAtributo;
};
//Categorys
'use strict';
const { Model } = require('sequelize');
module.exports = (sequelize, DataTypes) => {
class InmCategoria 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) {
// Categoria tiene varios atributos
InmCategoria.belongsToMany(models.InmAtributo, {
as: 'atributos',
through: models.InmAtributoCategoria,
foreignKey: 'categoria_id',
otherKey: 'atributo_id'
});
}
};
InmCategoria.init({
descripcion: DataTypes.STRING,
abreviado: DataTypes.STRING
}, {
sequelize,
modelName: 'InmCategoria',
tableName: 'inm_categorias',
timestamps: false,
});
return InmCategoria;
};
//AtributoCategoria
'use strict';
const { Model } = require('sequelize');
module.exports = (sequelize, DataTypes) => {
class InmAtributoCategoria 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) {
}
};
InmAtributoCategoria.init({
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
allowNull: false
},
valor: DataTypes.FLOAT,
fecha_activo: DataTypes.DATE,
}, {
sequelize,
modelName: 'InmAtributoCategoria',
tableName: 'inm_atributo_categoria',
timestamps: true,
});
return InmAtributoCategoria;
};
My migrations are:
//Attributes
'use strict';
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.createTable('inm_atributos', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
descripcion: {
allowNull: false,
type: Sequelize.STRING(100)
},
abreviado: {
allowNull: false,
type: Sequelize.STRING(5)
}
});
},
down: async (queryInterface, Sequelize) => {
await queryInterface.dropTable('inm_atributos');
}
};
//Categorys
'use strict';
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.createTable('inm_categorias', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
descripcion: {
allowNull: false,
type: Sequelize.STRING(100)
},
abreviado: {
allowNull: false,
type: Sequelize.STRING(5)
}
});
},
down: async (queryInterface, Sequelize) => {
await queryInterface.dropTable('inm_categorias');
}
};
//AtributoCategoria
'use strict';
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.createTable('inm_atributo_categoria', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
atributo_id: {
allowNull: false,
references: {
model: "inm_atributos",
key: "id"
},
type: Sequelize.BIGINT
},
categoria_id: {
allowNull: false,
references: {
model: "inm_categorias",
key: "id"
},
type: Sequelize.BIGINT
},
fecha_activo: {
allowNull: false,
type: Sequelize.DATE
},
valor: {
allowNull: false,
type: Sequelize.FLOAT
},
created_at: {
type: Sequelize.DATE
},
updated_at: {
type: Sequelize.DATE
}
});
},
down: async (queryInterface, Sequelize) => {
await queryInterface.dropTable('inm_atributo_categoria');
}
};
My function in controller:
async show(req, res) {
try {
const inmCategoria = await InmCategoria.findByPk(req.params.id, {
include: {
association: 'atributos',
through: {
attributes: ['categoria_id', 'atributo_id', 'valor', 'fecha_activo',
'updated_at', 'created_at']
}
}
});
return res.json({ 'status': 'Exito', 'body': inmCategoria })
} catch (error) {
return res.json({ 'status': 'Error', 'body': error.message })
}
},
Result
Query generated automatically
SELECT "InmCategoria"."id", "InmCategoria"."descripcion", "InmCategoria"."abreviado",
"atributos"."id" AS "atributos.id", "atributos"."descripcion" AS "atributos.descripcion",
"atributos"."abreviado" AS "atributos.abreviado", "atributos->InmAtributoCategoria"."id" AS
"atributos.InmAtributoCategoria.id", "atributos->InmAtributoCategoria"."categoria_id" AS
"atributos.InmAtributoCategoria.categoria_id", "atributos->InmAtributoCategoria"."atributo_id"
AS "atributos.InmAtributoCategoria.atributo_id", "atributos->InmAtributoCategoria"."valor" AS
"atributos.InmAtributoCategoria.valor", "atributos->InmAtributoCategoria"."fecha_activo" AS
"atributos.InmAtributoCategoria.fecha_activo", "atributos->InmAtributoCategoria"."updated_at"
AS "atributos.InmAtributoCategoria.updated_at", "atributos->InmAtributoCategoria"."created_at"
AS "atributos.InmAtributoCategoria.created_at" FROM "inm_categorias" AS "InmCategoria" LEFT
OUTER JOIN ( "inm_atributo_categoria" AS "atributos->InmAtributoCategoria" INNER JOIN
"inm_atributos" AS "atributos" ON "atributos"."id" = "atributos-
>InmAtributoCategoria"."atributo_id") ON "InmCategoria"."id" = "atributos-
>InmAtributoCategoria"."categoria_id" WHERE "InmCategoria"."id" = '10';
JSON output
{
"body": {
"id": "10",
"descripcion": "ZONE A01",
"abreviado": "A01",
"atributos": [
{
"id": "10",
"descripcion": "Additional wasteland",
"abreviado": "ABAL",
"InmAtributoCategoria": {
"categoria_id": "10",
"atributo_id": "10",
"valor": 50,
"fecha_activo": "2010-01-01",
"updated_at": "2021-10-06T19:11:27.000Z",
"created_at": "2021-10-06T19:11:27.000Z"
}
},
{
"id": "21",
"descripcion": "Environmental rate",
"abreviado": "TAMIN",
"InmAtributoCategoria": {
"categoria_id": "10",
"atributo_id": "21",
"valor": 120,
"fecha_activo": "2014-01-01",
"updated_at": "2021-10-06T19:11:27.000Z",
"created_at": "2021-10-06T19:11:27.000Z"
}
}
]
}
}
Query result in navicat
id descripcion abreviado atributos.id atributos.descripcion atributos.abreviado atributos.InmAtributoCategoria.id atributos.InmAtributoCategoria.categoria_id atributos.InmAtributoCategoria.atributo_id atributos.InmAtributoCategoria.valor atributos.InmAtributoCategoria.fecha_activo atributos.InmAtributoCategoria.updated_at atributos.InmAtributoCategoria.created_at
|id|descripcion|abreviado|atributos.id|atributos.descripcion|atributos.abreviado|atributos.InmAtributoCategoria.id|atributos.InmAtributoCategoria.categoria_id|atributos.InmAtributoCategoria.atributo_id|atributos.InmAtributoCategoria.valor|atributos.InmAtributoCategoria.fecha_activo|atributos.InmAtributoCategoria.updated_at|atributos.InmAtributoCategoria.created_at|
|10|ZONA A01|A01|10|ADICIONAL BALDIO|ABAL|167|10|10|60|2021-01-01|2021-10-06 16:11:27|2021-10-06 16:11:27|
|10|ZONA A01|A01|21|TASA AMBIENTAL MINIMO|TAMIN|175|10|21|265|2019-01-01|2021-10-06 16:11:27|2021-10-06 16:11:27|
|10|ZONA A01|A01|21|TASA AMBIENTAL MINIMO|TAMIN|179|10|21|800|2021-01-01|2021-10-06 16:11:27|2021-10-06 16:11:27|
Thanks! Cheers!
If you use category_id and attribute_id as foreign keys for the N:M join table then they cannot be the same in two different records because it violates N:M type of relationship between tables.
If you want to store several records with the same pair of values category_id and attribute_id then you need hasMany from InmCategoria to InmAtributoCategoria and belongsTo from InmAtributoCategoria to InmAtributo like this:
class InmCategoria extends Model {
...
static associate(models) {
InmCategoria.hasMany(models.InmAtributoCategoria, {
as: 'atributoCategoria',
foreignKey: 'categoria_id'
});
}
class InmAtributoCategoria extends Model {
...
static associate(models) {
InmAtributoCategoria.belongsTo(models.InmAtributo, {
as: 'atributo',
foreignKey: 'atributo_id'
});
}
And the query will look like this:
const inmCategoria = await InmCategoria.findByPk(req.params.id, {
include: {
association: 'atributoCategoria',
attributes: ['categoria_id', 'atributo_id', 'valor', 'fecha_activo',
'updated_at', 'created_at'],
include: {
association: 'atributo'
}
}
});

Node Sequelize inserting wrong field position on many to many table

I have a many to many relationship between the tables DeliveryOrder and GasBottle with the extra field Amount, but when i try to insert some values in this table, sequelize change´s the field order.
My models:
const {Model,DataTypes} = require ('sequelize')
class GasBottle extends Model{
static init(sequelize){
super.init({
type: DataTypes.INTEGER,
costPrice: DataTypes.DOUBLE,
sellPrice: DataTypes.DOUBLE
},{sequelize})
}
static associate({ DeliveryOrder,DeliveryOrderGasBottle}) {
GasBottle.belongsToMany(DeliveryOrder, {
through: DeliveryOrderGasBottle,
as: "deliveryOrders",
foreignKey: "deliveryOrderId",
});
}
}
module.exports = GasBottle
const { Model, DataTypes } = require("sequelize");
class DeliveryOrder extends Model {
static init(sequelize) {
super.init(
{
status: DataTypes.INTEGER,
latitude: DataTypes.INTEGER,
longitude: DataTypes.INTEGER,
delivererId: {
type: DataTypes.INTEGER,
references: {
model: "Deliverer",
key: "id",
},
},
},
{ sequelize }
);
}
static associate({ Deliverer, GasBottle, DeliveryOrderGasBottle }) {
DeliveryOrder.belongsTo(Deliverer, { foreignKey: "delivererId" });
DeliveryOrder.belongsToMany(GasBottle, {
through: DeliveryOrderGasBottle,
as: "bottles",
foreignKey: "gasBottleId",
});
}
}
module.exports = DeliveryOrder;
const { Model, DataTypes } = require("sequelize");
class DeliveryOrderGasBottle extends Model {
static init(sequelize) {
super.init(
{
gasBottleId: {
type: DataTypes.INTEGER,
references: {
model: "GasBottle",
key: "id",
},
},
deliveryOrderId: {
type: DataTypes.INTEGER,
references: {
model: "DeliveryOrder",
key: "id",
},
},
amount: DataTypes.INTEGER,
},
{ sequelize }
);
}
}
module.exports = DeliveryOrderGasBottle;
My migration file:
"use strict";
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable("DeliveryOrderGasBottle", {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER,
},
gasBottleId: {
type: Sequelize.INTEGER,
references: {
model: "GasBottle",
key: "id",
},
allowNull: false,
onDelete: "CASCADE",
},
deliveryOrderId: {
type: Sequelize.INTEGER,
references: {
model: "DeliveryOrder",
key: "id",
},
allowNull: false,
onDelete: "CASCADE",
},
amount:{
type: Sequelize.DOUBLE,
allowNull: false,
},
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable("DeliveryOrderGasBottle");
},
};
My association:
const deliveryOrder = await DeliveryOrder.create(obj);
bottlesToAdd.map(async (bottle) => {
const db_bottle = await GasBottle.findByPk(bottle.id);
await deliveryOrder.addBottle(db_bottle, {
through: { amount: parseInt(bottle.amount) },
});
On insert, sequelize is changing the order of the ids, resulting in a constraint error because i dont have the id´s on the respective tables
INSERT INTO "DeliveryOrderGasBottle" ("gasBottleId","deliveryOrderId","amount","createdAt","updatedAt") VALUES (18,1,42,'2020-06-27 19:17:26.204 +00:00','2020-06-27 19:17:26.204 +00:00')

How to use Sequelize belongsTo

I have simple sequelize models like below.
// user.js
module.exports = function(sequelize, DataTypes) {
const user = sequelize.define(
"User",
{
name: {
field: "name",
type: DataTypes.STRING(50),
unique: true,
allowNull: false
},
uid: {
field: "uid",
type: DataTypes.STRING(50),
allowNull: false
}
},
{
freezeTableName: true,
tableName: "user"
}
);
user.associate = function(models) {
user.hasMany(models.friend, {
foreignKey: "uid"
});
};
return user;
};
And there is another model.
// friend.js
module.exports = function(sequelize, DataTypes) {
const friend = sequelize.define(
"Friend",
{
uid: {
field: "uid",
type: DataTypes.STRING(50),
allowNull: false
},
jsonId: {
field: "json-id",
type: DataTypes.STRING(50),
allowNull: true
},
nlpId: {
field: "nlp-id",
type: DataTypes.STRING(50),
allowNull: true
}
},
{
freezeTableName: true,
tableName: "friend"
}
);
friend.associate = function(models) {
friend.belongsTo(models.user, { foreignKey: "uid" });
};
return friend;
};
And there is index.js. When I run sequelize, it gives me an error like "Error: Friend.belongsTo called with something that's not a subclass of Sequelize.Model".
Could you recommend some advice for this problem? Thank you so much for reading it.
db.user = require('./user')(sequelize, Sequelize);
db.friend = require('./friend')(sequelize, Sequelize);
Write to the user model
User.associate = models => {
User.hasMany(models.Friend, {
as: 'friends',
foreignKey: 'userId'
});
};
Write to the friend model
Friend.associate = models => {
Friend.belongsTo(models.User, {
as: 'friend',
foreignKey: 'userId'
});
};

Many to Many Relationship Sequelize Node JS Array of Attributes

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.

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