Sequelize - many to many relationship - node.js

I have multiple tables relating to each other, what i want to do now is create a many to many relationship between Document and Tag.
A single document can have many tags and a single Tag can have many Documents, How can i do this many to many relationship in sequelize?
In models/index.js i have:
Tab.sync().then(() => {
Group.sync().then(() => {
User.sync().then(() => {
Document.sync().then(() => {
Reference.sync().then(() => {
Tag.sync().then(() => {
User.hasMany(Group, {foreignKey: 'userAssigned', as: 'groups'})
Group.belongsTo(User, {foreignKey: 'userAssigned', as: 'user'})
Document.belongsTo(Group, {foreignKey: 'groupId', as: 'group'})
Group.hasMany(Document, {foreignKey: 'groupId', as: 'documents'})
Tab.hasMany(Group, {foreignKey: 'tabId', as: 'groups'})
Group.belongsTo(Tab, {foreignKey: 'tabId', as: 'tab'})
Document.hasMany(Reference, {foreignKey: 'docId', as: 'references'})
Reference.belongsTo(Document, {foreignKey: 'docId', as: 'docs'})
//trying to create many to many relationship here//
Document.hasMany(Tag)
Tag.hasMany(Document)
//---------------------//
})
})
})
})
})
})
ps: i've already read about through parameter, but i cannot understand how it would work

Many-to-many relations are described using belongsToMany in Sequelize:
// Do you already have the DocumentTag model and a table?
// I'm assuming here DocumentTag has docId and tagId fields
Document.belongsToMany(Tag, { through: DocumentTag, foreignKey: 'docId', otherKey: 'tagId' })
Tag.belongsToMany(Document, { through: DocumentTag, foreignKey: 'tagId', otherKey: 'docId' })
To get documents along with linked tags you can query like this:
const docs = await database.Document.findAll({
where: {
// here are your search conditions
},
include: [database.Tag]
})
To add tags to a certain document you can call the addTags model instance method:
const tags = await database.Tag.findAll({
where: {
// here are your search conditions
},
// to link tags to documents we don't need all attributes but `id`
attributes: ['id']
})
const doc = await database.Document.findById(documentId)
await doc.addTags(tags)

Related

getting the follwerList from the through table (sequelize)

Currently making a sns project.
Have a user model and made a N:M association which tells you who is following who.
So there is a connected models between 'user' and 'user'.
This is how my code looks like
static associate(db) {
db.User.hasMany(db.Post);
db.User.belongsToMany(db.User, {
foreignKey: 'followingId',
as: 'Followers',
through: 'Follow',
});
db.User.belongsToMany(db.User, {
foreignKey: 'followerId',
as: 'Followings',
through: 'Follow',
});
}
and I'm trying to show how many followers and following the user has at the profile page.
So what I did is when the /main pass
const User = require('../models/user');
router.use((req, res, next) => {
res.locals.followingList = User.findAll({
where : {followerId : req.user}
});
next();
});
Having a problem accessing the data from through table.
Having a problem accessing the data from through table.
If we want to get the user along with followings and followers then you need to use findOne (or findByPk if req.user is a primary key value) because we want a single user and just include both associations in the query though I don't recommend to include more than one M:N associations to the same query:
res.locals.followingList = await User.findOne({
where : {id : req.user},
include: [{
model: User,
as: 'Followers'
}, {
model: User,
as: 'Followings'
}]
});

How do I put a Sequelize model and its assosications into one file?

I found that if I don't put all the associations (hasMany, etc) into one file, I get the following error.
throw new Error(`${this.name}.belongsToMany called with something that's not a subclass of Sequelize.Model`);
^
Error: users.belongsToMany called with something that's not a subclass of Sequelize.Model
at Function.belongsToMany (C:\app\node_modules\sequelize\lib\associations\mixin.js:49:13)
at Object.<anonymous> (C:\app\models\/user.ts:51:6)
According to this post, this can be solved by putting all the associations into one file.
Still, I don't think it's a good approach, because
If you want to know about a model, you have to check the model definition (models/user.ts in the below example) and the association file (something like models/index.ts).
the association file can be quite huge if you have many models with associations.
How do I put a Sequelize model and its associations into the same file?
Here's what I'm trying to achieve.
// `models/user.ts`
import { Role } from './role';
const User = sequelizeInstance.define<UserInstance>(
'users', {/* fields */},
);
User.belongsToMany(Role, {
through: 'user_roles',
foreignKey: 'userId',
otherKey: 'roleId',
});
export { User };
// `model/role.ts`.
import { User } from './user';
const Role = sequelizeInstance.define<RoleInstance>(
'roles', {/* fields */}
);
Role.belongsToMany(User, {
through: 'user_roles',
foreignKey: 'userId',
otherKey: 'roleId',
});
export { Role };
Any advice will be appreciated.
Here is what I did.
I declare each model associations in the model declaration, using associate property. In your case something like:
const Role = sequelizeInstance.define<RoleInstance>(
'roles', {/* fields */}
);
Role.associate = function (models) {
Role.belongsToMany(models.users, {
through: 'user_roles',
foreignKey: 'userId',
otherKey: 'roleId',
});
});
Then in my index file, I wrote few lines to fetch all associations from models declaration and apply them:
db.roles = // assign your Role model
db.users = // assign your User model
// setup table associations
Object.keys(db).forEach(function (modelName) {
if ('associate' in db[modelName]) {
// call the associate function and pass reference to all other models
db[modelName].associate(db);
}
});
In this way I can keep a compact index, fetch and apply associations dynamically and declare associations in each model
My answer to a similar question asked a few years ago:
https://stackoverflow.com/a/67875061/11558646
Assuming the approach is sound, its advantage is that it doesn't require separate "setting-up-of-all-associations" logic.

ORM: Sequelize: many to many relationship query

How do I query in many to many relationships? e.g. There are product, category, and product_category models. Below is an association by model:
// product
product.belongsToMany (models.category, {
through: 'product_category',
foreignKey: 'product_id'
});
// category
category.belongsToMany (models.product, {
through: 'product_category',
foreignKey: 'category_id'
});
// product_category
product_category.belongsTo (models.product, {
foreignKey: 'product_id'
});
product_category.belongsTo (models.category, {
foreignKey: 'category_id'
});
I would appreciate if you can tell me which model the findAll () should have at the beginning when querying.
Yep, seems like a good fit if you are using node and sequelize it’s a pretty simple lift to put GraphQL in play and manipulate your sequelize models in your resolvers... nice to have the graphiql playground to hit your queries with.... easy peazy

Sequelize does not create hasMany associations but foreign keys defined in DB

Altough it seems my associations defined in the database when I call create on my manufacturer model associations does not create.
These are my associated models.
ManufacturerText.associate = function (models) {
models.manufacturer_text.belongsTo(models.language, {
as: 'language'
});
models.manufacturer_text.belongsTo(models.manufacturer, {
as: 'manufacturer'
});
};
ManufacturerVideo.associate = function (models) {
models.manufacturer_video.belongsTo(models.language, {
as: 'language'
});
models.manufacturer_video.belongsTo(models.video_type, {
as: 'video_type'
});
models.manufacturer_video.belongsTo(models.manufacturer, {
as: 'manufacturer'
});
}
And this is the main model:
```
Manufacturer.associate = function(models) {
// models
models.manufacturer.hasMany(models.manufacturer_text, {foreignKey:'manufacturer_id', as: 'manufacturer_translations' });
models.manufacturer.hasMany(models.manufacturer_video, {foreignKey:'manufacturer_id', as: 'manufacturer_videos' });
models.manufacturer.hasMany(models.inspiration_image, {foreignKey:'manufacturer_id', as: 'inspirations' });
models.manufacturer.belongsTo(models.file, {as: 'image'});
models.manufacturer.belongsTo(models.file, {as: 'header_image'});
};
none of the two associations above work.
When I inspect DB with MySQL Workbench it seems associations defined properly.
It seems the problem caused by the input.
When you send id attribute even doesn't matter if its null, sequelize do not understand that it is an INSERT
I was sending a record like below;
{
delivery_days: 10,
image_id: 4,
{ id: null, url: 'http://url', ... }
}

How to find data of source table from target table in sequelize?

I have two tables, traders, and messages
Traders is associated to messages as following
traders.hasMany(models.messages, {as: 'sender',foreignKey : 'senderId'});
traders.hasMany(models.messages, {as: 'reciever',foreignKey : 'recieverId'});
now when I try to find trader name along with all messages using following code
ctx.body = await ctx.db.messages.findAll({
include:[{
model: ctx.db.traders,
as:'sender'
}],
attributes:['type',['data','message'],'createdAt','senderId','name'],
where:{
conversationId:ctx.request.body.conversationId
}
})
I get the following error
SequelizeEagerLoadingError: traders is not associated to messages!
Try association from both models like,
traders.hasMany(models.messages, {
as: 'sender',
foreignKey: 'senderId'
});
messages.belongsTo(models.traders, {
as: 'sender',
foreignKey: 'senderId'
});

Resources