Node Sequelize: How to include same table into 2 columns - node.js

I have 2 table:
User
Follow
Table follow has 2 foreign keys:
follower_id
followee_id
They are both foreign key to user table
I have the following associations created:
//Users-Followers
this.follows.belongsTo(this.users, {
as: 'follower',
foreignKey: 'follower_id'
});
this.users.hasMany(this.follows, {
as: 'folower',
foreignKey: 'follower_id'
});
//Users-Followees
this.follows.belongsTo(this.users, {
as: 'folowee',
foreignKey: 'followee_id'
});
this.users.hasMany(this.follows, {
as: 'folowee',
foreignKey: 'followee_id'
});
Now I want to: get who is following who.
At the following is my query:
async function getFollowByFollower(follower_id) {
let follows = model.follows;
let users = model.users;
let follow = await follows.findAll({
include: [
{
model: users,
as: 'follower',
required: true,
where: {
id: follower_id
}
},
{
model: users,
as: 'followee',
required: true
}
]
}).catch(function(err) {
console.log('followsDAO.js getFollowByFollower error: ' + err);
});
return follow;
};
Here goes the error message:
SequelizeEagerLoadingError: users is associated to follows multiple times. To identify the correct association, you must use the 'as' keyword to specify the alias of the association you want to include.
As you can see, I have clearly indicated 'as' in both query and association.
Please help.
Thanks.
Best regards,

Never mind. My code logic is 100% correct. The problem is in the association statement I misspelled "follower" as "folower" and "followee" as "folowee".
After correcting the spelling the code is running.

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

Sequelize many to many with extra columns

After some research I didn't find anything related to my problem. So the setting is an M:M relationship already working with sequelize (sqllite):
return User.find({ where: { _id: userId } }).then(user => {
logger.info(`UserController - found user`);
Notification.find({ where: { _id: notificationId } }).then(notification => {
if (associate) {
return user.addNotification([notification]);
} else {
return user.removeNotification([notification]);
}
})
})
The thing is that I have extra fields in the inter table(cityId, active) and I don't know how to update it when running "addNotification".
Thanks in advance
If you are using Sequelize version 4.x there is some changes in the API
Relationships add/set/create setters now set through attributes by passing them as options.through (previously second argument was used as through attributes, now its considered options with through being a sub option)
user.addNotification(notification, { through: {cityId: 1, active: true}});
In order to add data to pivot table you should pass data as second parameter of add function
user.addNotification(notification, {cityId: 1, active: true});
When the join table has additional attributes, these can be passed in the options object:
UserProject = sequelize.define('user_project', {
role: Sequelize.STRING
});
User.belongsToMany(Project, { through: UserProject });
Project.belongsToMany(User, { through: UserProject });
// through is required!
user.addProject(project, { through: { role: 'manager' }});
You can find more about this here: https://sequelize.org/master/class/lib/associations/belongs-to-many.js~BelongsToMany.html

How do I reference an association when creating a row in sequelize without assuming the foreign key column name?

I have the following code:
#!/usr/bin/env node
'use strict';
var Sequelize = require('sequelize');
var sequelize = new Sequelize('sqlite:file.sqlite');
var User = sequelize.define('User', { email: Sequelize.STRING});
var Thing = sequelize.define('Thing', { name: Sequelize.STRING});
Thing.belongsTo(User);
sequelize.sync({force: true}).then(function () {
return User.create({email: 'asdf#example.org'});
}).then(function (user) {
return Thing.create({
name: 'A thing',
User: user
}, {
include: [User]
});
}).then(function (thing) {
return Thing.findOne({where: {id: thing.id}, include: [User]});
}).then(function (thing) {
console.log(JSON.stringify(thing));
});
I get the following output:
ohnobinki#gibby ~/public_html/turbocase1 $ ./sqltest.js
Executing (default): INSERT INTO `Users` (`id`,`email`,`updatedAt`,`createdAt`) VALUES (NULL,'asdf#example.org','2015-12-03 06:11:36.904 +00:00','2015-12-03 06:11:36.904 +00:00');
Executing (default): INSERT INTO `Users` (`id`,`email`,`createdAt`,`updatedAt`) VALUES (1,'asdf#example.org','2015-12-03 06:11:36.904 +00:00','2015-12-03 06:11:37.022 +00:00');
Unhandled rejection SequelizeUniqueConstraintError: Validation error
at Query.formatError (/home/ohnobinki/public_html/turbocase1/node_modules/sequelize/lib/dialects/sqlite/query.js:231:14)
at Statement.<anonymous> (/home/ohnobinki/public_html/turbocase1/node_modules/sequelize/lib/dialects/sqlite/query.js:47:29)
at Statement.replacement (/home/ohnobinki/public_html/turbocase1/node_modules/sqlite3/lib/trace.js:20:31)
It seems that specifying {include: [User]} instructs Sequelize to create a new User instance matching the contents of user. That is not my goal. In fact, I find it hard to believe that such behaviour would ever be useful—I at least have no use for it. I want to be able to have a long-living User record in the database and at arbitrary times create new Things which refer to the User. In my shown example, I wait for the User to be created, but in actual code it would likely have been freshly loaded through User.findOne().
I have seen other questions and answers say that I have to explicitly specify the implicitly-created UserId column in my Thing.create() call. When Sequelize provides an API like Thing.belongsTo(User), I shouldn’t have to be aware of the fact that a Thing.UserId field is created. So what is the clean API-respecting way of creating a new Thing which refers to a particular User without having to guess the name of the UserId field? When I load a Thing and specify {include: [User]}, I access the loaded user through the thing.User property. I don’t think I’m supposed to know about or try to access a thing.UserId field. In my Thing.belongsTo(User) call, I never specify UserId, I just treat that like an implementation detail I shouldn’t care about. How can I continue to avoid caring about that implementation detail when creating a Thing?
The Thing.create() call that works but looks wrong to me:
Thing.create({
name: 'A thing',
UserId: user.id
});
Option 1 - risks DB inconsistency
Sequelize dynamically generates methods for setting associations on instances, e.g. thing.setUser(user);. In your use case:
sequelize.sync({force: true})
.then(function () {
return Promise.all([
User.create({email: 'asdf#example.org'}),
Thing.create({name: 'A thing'})
]);
})
.spread(function(user, thing) {
return thing.setUser(user);
})
.then(function(thing) {
console.log(JSON.stringify(thing));
});
Option 2 - does not work/buggy
It isn't documented, but from a code dive I think the following should work. It doesn't but that seems to be because of a couple of bugs:
// ...
.then(function () {
return models.User.create({email: 'asdf#example.org'});
})
.then(function(user) {
// Fails with SequelizeUniqueConstraintError - the User instance inherits isNewRecord from the Thing instance, but it has already been saved
return models.Thing.create({
name: 'thingthing',
User: user
}, {
include: [{
model: models.User
}],
fields: ['name'] // seems nec to specify all non-included fields because of line 277 in instance.js - another bug?
});
})
Replacing models.User.create with models.User.build doesn't work because the built but not saved instance's primary key is null. Instance#_setInclude ignores the instance if its primary key is null.
Option 3
Wrapping the Thing's create in a transaction prevents an inconsistent state.
sq.sync({ force: true })
.then(models.User.create.bind(models.User, { email: 'asdf#example.org' }))
.then(function(user) {
return sq.transaction(function(tr) {
return models.Thing.create({name: 'A thing'})
.then(function(thing) { return thing.setUser(user); });
});
})
.then(print_result.bind(null, 'Thing with User...'))
.catch(swallow_rejected_promise.bind(null, 'main promise chain'))
.finally(function() {
return sq.close();
});
I have uploaded a script demo'ing option 2 and option 3 here
Tested on sequelize#6.5.1 sqlite3#5.0.2 I can use User.associations.Comments.foreignKey as in:
const Comment = sequelize.define('Comment', {
body: { type: DataTypes.STRING },
});
const User = sequelize.define('User', {
name: { type: DataTypes.STRING },
});
User.hasMany(Comment)
Comment.belongsTo(User)
console.dir(User);
await sequelize.sync({force: true});
const u0 = await User.create({name: 'u0'})
const u1 = await User.create({name: 'u1'})
await Comment.create({body: 'u0c0', [User.associations.Comments.foreignKey]: u0.id});
The association is also returned during creation, so you could also:
const Comments = User.hasMany(Comment)
await Comment.create({body: 'u0c0', [Comments.foreignKey]: u0.id});
and on many-to-many through tables you get foreignKey and otherKey for the second foreign key.
User.associations.Comments.foreignKey contains the foreignKey UserId.
Or analogously with aliases:
User.hasMany(Post, {as: 'authoredPosts', foreignKey: 'authorId'});
Post.belongsTo(User, {as: 'author', foreignKey: 'authorId'});
User.hasMany(Post, {as: 'reviewedPosts', foreignKey: 'reviewerId'});
Post.belongsTo(User, {as: 'reviewer', foreignKey: 'reviewerId'});
await sequelize.sync({force: true});
// Create data.
const users = await User.bulkCreate([
{name: 'user0'},
{name: 'user1'},
])
const posts = await Post.bulkCreate([
{body: 'body00', authorId: users[0].id, reviewerId: users[0].id},
{body: 'body01', [User.associations.authoredPosts.foreignKey]: users[0].id,
[User.associations.reviewedPosts.foreignKey]: users[1].id},
])
But that syntax is so long that I'm tempted to just hardcode the keys everywhere.

What is populate in SailsJs?

I recently began to develop on sailsJs and not understanding the subtleties
Please explain to me what is populate in SailsJs and who can please do simple example
Thanks in advance ?
whats is ?
User.find({ name: 'foo' })
.populate('pets', { name: 'fluffy' })
.exec(function(err, users) {
if(err) return res.serverError(err);
res.json(users);
});
populate is used for associations. When your model is something like this:
// User.js
module.exports = {
attributes: {
name: {
type: "string"
},
pet: {
model: "pet"
}
}
}
Here pet attribute of user collection is a reference to pet table. In user table it will store only the id column of pet. However, when you do a populate while find, then it will fetch the entire record of the pet entry and display it here. This is just for one to one association. You can have many to one associations as well as many to many. See this documentation for more details
sailsjs project is use the wateline ORM. you can see the document. if you want to use 'populate()', you need define Associations in the model.
.populate()
populate is used with associations to include any related values specified in a model definition. If a collection attribute is defined in a many-to-many, one-to-many or many-to-many-through association the populate option also accepts a full criteria object. This allows you to filter associations and run limit and skip on the results.
as you example, you need do like this:
User.js
// A user may have many pets
var User = Waterline.Collection.extend({
identity: 'user',
connection: 'local-postgresql',
attributes: {
firstName: 'string',
lastName: 'string',
// Add a reference to Pets
pets: {
collection: 'pet',
via: 'owner'
}
}
});
Pet.js
// A pet may only belong to a single user
var Pet = Waterline.Collection.extend({
identity: 'pet',
connection: 'local-postgresql',
attributes: {
breed: 'string',
type: 'string',
name: 'string',
// Add a reference to User
owner: {
model: 'user'
}
}
});
you can ready the doc, and you can use it very easy
To resume what is .populate() (used by waterline) is a little what join is used by SQL.
.populate() allows you to join the tables in your database.
The link identifier is to be defined in your model.
In other words, to associate a user (who is in the "User" table) with a dog (who is in the "Dog" table), you use populate.
To resume your example:
You are looking for the user => User.find ({name: my_user})
You are looking for the dog named "fluffy" => {name: 'fluffy'}
You are looking for the dog 'fluffy' which is associated with your user (belongs) => populate ('pets')
Which give:
User.find({ name: 'foo' })
.populate('pets', { name: 'fluffy' })
.exec(function(err, users) {
if(err) return res.serverError(err);
res.json(users);
}
This association ("pets"), you define it in your models "User" and "Pets" like the example above.
Populate is all about displaying the content of the (id) on which it was refreed
"abc": [{
type: ObjectId,
ref: "xyz"
}],

Resources