Sequelize findall for every findall is possible? - node.js

I have this tables. Clients have Projects and Users works in Projects
Clients
- id
- name
Projects
- id
- name
- client_id
Users
- id
- name
UserProject
- user_id
- project_id
I try to return all users of the every project of client for example id=1
Finally result, something like this JSON:
[{
id:1
name:"Project1"
users:[{
id:23
name:"Robert Stark"
},{
id:67
name: "John Snow"
}]
}, {
id:2
name:"Project2"
users:[{
id:1
name:"Aria Stark"
}]
}]
If I find projects it works fine
req.tables.Project.findAll({
where: {
client_id:1
}
}).success(function(projects) {
...
If I find Users of a project it works fine
req.tables.UserProject.findAll({
where: {
project_id:1
},
include: [
{ model: req.tables.User, as: 'User' }
]
}).success(function(UsersProject) {
...
But, how can I combine both finAlls to return all users in every project? Something like the next code, but that works well. How can I do it?
I found this: Node.js multiple Sequelize raw sql query sub queries but It doesn't work for me or I do not know how to use it, because I have 2 loops not only one. I have projects loop and users loop
req.tables.Project.findAll({
where: {
client_id:1
}
}).success(function(projects) {
var ret_projects=[];
projects.forEach(function (project) {
var ret_project={
id:project.id,
name:project.name,
data:project.created,
users:[]
});
req.tables.UserProject.findAll({
where: {
project_id:project.id
},
include: [
{ model: req.tables.User, as: 'User' }
]
}).success(function(UsersProject) {
var ret_users=[];
UsersProject.forEach(function (UserProject) {
ret_users.push({
id:UserProject.user.id,
name:UserProject.user.name,
email:UserProject.user.email
});
});
ret_project.users=ret_users;
ret_project.push(ret_project)
});
});
res.json(projects);
});

Sounds like you already have a solution, but I came across the same issue and came up with this solution.
Very similar to what cvng said, just using nested include. So use:
Project.belongsTo(Client);
Project.hasMany(User);
User.hasMany(Project);
Then:
req.tables.Client.find({
where: { id:req.params.id },
include: [{model: req.tables.Project, include : [req.tables.User]}]
}).success(function(clientProjectUsers) {
// Do something with clientProjectUsers.
// Which has the client, its projects, and its users.
});
}
The ability to 'Load further nested related models' is described through the param 'option.include[].include' here: Sequelize API Reference Model.
Maybe this will be useful to someone else in the future.
Cheers!

I think you would not have to query UserProject entity directly but instead use Sequelize Eager loading methods to retrieve your entities.
Your models associations should look something like this :
Project.belongsTo(Client);
Project.hasMany(User, { as: 'Workers' });
User.hasMany(Project);
and once you have all projects related to client, your finder method :
Project
.findAll({ include: [{ model: User, as: 'Workers' })
.success(function(users) {
// do success things here
}
Take a look at, http://sequelizejs.com/docs/1.7.8/models#eager-loading.
Hope it helps !

Finally!!
cvng, your example helpme a lot, thanks.
But I have 3 levels Client, Project, thi is my final solution, is this a good solution?
req.tables.Client.find({
where: { id:req.params.id },
include: [{ model: req.tables.Project, as: 'Projects' }]
}).success(function(client) {
var ret ={
id:client.id,
name:client.name,
projects:[]
};
done = _.after(client.projects.length, function () {
res.json(ret);
});
client.projects.forEach(function (project) {
project.getUsers().success(function(users) {
var u=[]
users.forEach(function (user) {
u.push({
id:user.id,
name:user.name,
});
});
ret.projects.push({
id:project.id,
name:project.name,
users:u
});
done();
});
});
});

Related

How to create a delete method in nodejs(express) with sequelize(sqlite) as database

I have been trying yesterday and still continuing today to figure out how to create a nodejs delete method so I can delete data from database based on ID.
I have tried different code from google/youtube/stackoverflow etc but nothing has worked so far.
The error I have with this code is that data.query is not a function. Data is a variable on my code.
If anyone has any idea how to fix please help.
app.js
app.delete('/zoom/:id', function(req, res) {
data.query('delete from', [req.params.id]);
res.render('deleted')
});
data.js
var Data = sequelize.define('data', {
subject: Sequelize.STRING,
MEETINGID: Sequelize.STRING,
Password: Sequelize.STRING
});
Sequelize destroy method seems like a suitable one. E.g.
app.delete("/zoom/:id", function (req, res) {
data.destroy({
where: {
// criteria
},
});
res.render("deleted");
});
To set a criteria which i suitable for your situation, you will need to take a look at sequelize syntax. I found some examples and may be you can modify them to your needs. Depending on your database structure.
where: {
'$car.id$': 2
},
where: {
furniture_type: 'leather'
},
where: {
id: {
$notLike: { $any: someValue }
}
},

How to query in sequelize by an association without loading the associated object?

I have multiple models which are associated to each other.
e.g:
var User = sequelize.define("user")
var Project = sequelize.define("project")
Project.hasMany(User)
Now I want to query all Projects containing a specific user.
e.g.:
Project.findAll({
include: [
{
model: User,
where: { id }
}
]
})
This works, but loads also the user and attaches it to the project.
How can I tell sequelize, that the user should not be added to the found projects?
I just managed to address the same problem (using Sequelize 4).
You can specify that you don't want any fields of User just using attributes: [], so your code would become:
Project.findAll({
include: [
{
attributes: [],
model: User,
where: { id }
}
]
})
Many-to-many relationships are defined using the belongsToMany() method in sequelize on both sides. For your specific use case you would have to use a through model for the relation and query the through model directly.
var User = sequelize.define("user")
var Project = sequelize.define("project")
var ProjectUser = sequelize.define("projectUser")
Project.belongsToMany(User, {
through: ProjectUser
})
User.belongsToMany(Project, {
through ProjectUser
});
ProjectUser.findAll({
where: {
UserId: 'someId'
},
// We only want the project, not the user.
// You might need to do ProjectUser.belongsTo() for both
// models for this to work.
include: [Project]
})
.then(function(results) {
// Here we are getting an array of ProjectUsers, to return all the projects
// we map it to a new array of only projects.
return results.map(function(userProject) {
return userProject.Project;
});
})

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

Sails:How to join two different models using waterline

In MVC peoples are using join query to join the two different tables, but In sails.js what I have to use? There is any method in waterline?
The answer based on database you are using.
For instance, you need to populate values in Mongo not to join. Or you need to join tables if you are using MySQL or similar.
In a nutshell, all this stuff is covered via Waterline. So you can just declare model in api/models with associations. Joining and populating is executing under the Waterline adapter.
For instance, you have User and Comment.
// api/models/User.js
module.exports = {
attributes: {
name: {
type: 'string'
},
comments: {
collection: 'Comment',
via: 'user'
}
}
};
// api/models/Comment.js
module.exports = {
attributes: {
text: {
type: 'string'
},
user: {
model: 'User',
via: 'comments'
}
}
};
Then you are execute User.find() and get already joined\populated tables from database.
But, if you want to execute manual joining, you can use .populate() method on Model instance. For instance:
// api/controllers/AnyController.js
module.exports = {
action: function(req, res) {
User
.findOne('ID_HERE')
.populate('comments')
.then(function(result) {})
.catch(function(error) {});
}
};
You can read more about populate here - http://sailsjs.org/documentation/reference/waterline-orm/queries/populate

Node.js How to republish the data.

We have published items based on list ids , we use myLists variable to filter out the List Id, but this variable is not reactive by nature ,when we try to add the new list , then items of new list are not publishing automatically.
Meteor.publish('subscription_items', function () {
var userName = this.userId ? Meteor.users.find({ _id: this.userId }).fetch()[0].username : null;
var myLists = [];
var sharedListIDs = [];
SharedLists.find({ shared_with: userName }).forEach(function (list) {
sharedListIDs.push(list.list_id);
});
Lists.find({ $or: [{ owner: userName }, { _id: { $in: sharedListIDs } }] }).forEach(function (list) {
myLists.push(list._id);
});
return Items.find({ list_id: { $in: Lists.find({ $or: [{ owner: userName }, { _id: { $in: sharedListIDs } }] }).fetch() } });.
Can we have any way to always publish fresh data. Please help me to resolve this issue. any help/suggestion would appreciate.
As David Weldon pointed out in this answer to my similar question, what you are looking for is a reactive join. By using the package publish-with-relations, I think the publish function you want looks something like this:
Meteor.publish('subscription_items', function() {
return Meteor.publishWithRelations({
handle: this,
collection: Lists,
filter: {owner: this.userId},
mappings: [{collection: SharedLists, key: 'list_id'}]
});
});
Alternatively, as a (dirty) workaround, you could call
Meteor.subscribe('subscription_items');
everywhere where you need the collection to be republished.

Resources