I have 3 models. User, Profile and comments.
Profile is an association of User (one to one) and comments are an association of Profile (one to many).
User Model:
attributes: {
profile: {
model: 'Profile'
},
}
Profile Model:
attributes: {
comments: {
collection: 'profileComment',
via: 'profile'
}
}
Comments model:
attributes: {
profile: {
model: 'Profile'
},
}
Getting the user profile works fine:
User.findOneById(id)
.populate('profile')
.exec(function (err, user) {
// user.profile
});
But then how would I populate the profile with the comments?
It seems like you could back into what you want by setting a user attribute on profile:
attributes: {
comments: {
collection: 'profileComment',
via: 'profile'
},
user: {
model: 'User'
}
}
And then querying with:
Profile.findOne({user: userId})
.populate('user')
.populate('comments')
.exec(function(err, profile) {
// use profile.user and profile.comments
});
Keep in mind, however, that Waterline doesn't currently implement true one-to-one associations, so if you set a Profile instance's user attribute to 123, the corresponding User instance won't automatically have its profile attribute set. This may not be a big deal--you can always look up Profile and populate User, like in the example above--but it's something to keep in mind.
Your other option is to keep things as they are and do a mapping, as in this question and answer.
Related
I have a problem creating foreign key onDelete constraint using Sequelize on MSSQL db.
This is my use case: each company can have multiple users and those users can create multiple requests. Some requests can be created without a reference to the user, which is why I have both references to company and user in the request model. I want to achieve that once a company is deleted, all users from the company are deleted and all requests from the company are deleted.
These are my models defined in Sequelize:
Company model:
const CompanyModel = sequelize.define('company', {
... some props ...
});
CompanyModel.associate = function (models) {
CompanyModel.hasMany(models.user, {
as: 'users',
foreignKey: 'companyId',
onDelete: 'CASCADE',
onUpdate: 'CASCADE',
});
CompanyModel.hasMany(models.request, {
as: 'requests',
foreignKey: 'companyId',
onDelete: 'CASCADE',
onUpdate: 'CASCADE',
});
};
User model:
const UserModel = sequelize.define('user', {
... some props ...
});
UserModel.associate = function (models) {
UserModel.belongsTo(models.company, {
as: 'company',
foreignKey: 'companyId',
});
UserModel.hasMany(models.request, {
as: 'requests',
foreignKey: 'userId',
});
};
Request model:
const RequestModel = sequelize.define('request', {
... some props ...
});
RequestModel.associate = function (models) {
RequestModel.belongsTo(models.company, {
as: 'company',
foreignKey: 'companyId',
});
RequestModel.belongsTo(models.user, {
as: 'user',
foreignKey: 'userId',
});
};
However, when I try to sync my database, this is the error I'm getting:
RequestError: Could not create constraint or index. See previous errors
'IF OBJECT_ID(\'[request]\', \'U\') IS NULL CREATE TABLE [request] ([id] CHAR(36) , ... some props ..., [companyId] CHAR(36) NULL, [userId] CHAR(36) NULL, PRIMARY KEY ([id]), FOREIGN KEY ([companyId]) REFERENCES [company] ([id]) ON DELETE CASCADE, FOREIGN KEY ([userId]) REFERENCES [user] ([id]) ON DELETE SET NULL);'
If I comment the relationship to the user inside of the request model then syncing of the databases passes. The same thing happens if I comment onDelete foreign key constraint inside of the company model for the user. The foreign key constraint for request doesn't matter. I guess, the problem is that deleting company will delete user which might be the creator of the request. The logic is that those requests will be deleted as well, but the database cannot know if only users from the same company (which will be deleted) will be the owners of requests deleted as well for the company.
I've also tried setting allowNull to foreign key for the user in request model, but it didn't work.
Any suggestions on what should I do? I need to achieve that deleting a company deletes its users and requests.
I have a model Post:
// Post.js
attributes:{
name: {type:"string"},
users: { collection: 'user', via: 'postsReceived' },
category: { model: 'category' },
// Plus lots of other fields and relationships to other models for example category, topic, etc...
}
And I have a model Users:
// Users.js
attributes:{
name: {type:"string"},
postsReceived: { collection: 'post', via: 'users', dominant: true }
}
A post can be sent to many users and a user may have many posts. This if fine however when it comes to populating a list of posts that a user is attached to this becomes quite difficult. I am using the below code to get the list of posts, however this does not populate the posts relationships. For example the below will only return a id for the category of the post. What is the best way (in terms of performance) to get this to work?
User.findOne({id:id})
.populate('postsReceived')
.exec(function(err, user){
if(err) return res.serverError(err);
if (user){
var result = user.postsReceived;
return res.json(result);
}
});
I found a solution for this that seems to work quite nicely from:
This Answer
The answer in the question above suggets using sails-hook-orm-offshore
I am trying to get all of my posts' comments which comments count is greater than 0. I am tying to add the where in find and populate, but none of it worked.
Post model:
module.exports = {
attributes: {
user: {
model: 'user'
},
comments: {
collection: 'comment',
via: 'post'
}
};
PostController:
Post
.find({comments: {'>': 0}, user: me})
.populate('comments')
.exec(function(err, comments){
res.json(comments);
});
In current Sails/Waterline relations implementation you can't filter by one-to-many related fields. You need to filter the result after find is completed.
Hey all just getting started with Sails js and mongoDB and im a bit confused.
I have two models with a many-to-many relationship:
User.js
module.exports = {
attributes: {
username: 'STRING',
password: 'STRING',
doors:{
collection: 'door',
via: 'users',
}
}
};
and Door.js
module.exports = {
attributes: {
name: 'STRING',
users:{
collection: 'user',
via: 'doors'
}
}
};
This works fine, I can create a user and a door and associate one with the other. However I'd like to have another field in the join, an expiry date (Say the user can only have access to a particular door until a particular date).
How would I go about doing that?
You need to create a many to many through association. However they are not officially supported as of yet.
You can manually do this however.
Now, in this example it may sometimes be a a little more difficult to get all the doors for a user and vice versa, because you have to preform a second look up. However you can do the following with this setup:
UserDoors
.find()
.where({expires:{'<':new Date()}})
.populate('doors')
.populate('users')
.exec(/*....*/)
Your models
User.js
module.exports = {
attributes: {
username: 'STRING',
password: 'STRING',
doors:{
collection: 'userDoors',
via: 'users',
}
}
};
userDoors.js
module.exports = {
attributes: {
doors:{
model: 'door'
},
users:{
model: 'user'
},
expires: 'datetime'
}
};
and Door.js
module.exports = {
attributes: {
name: 'STRING',
users:{
collection: 'userDoors',
via: 'doors'
}
}
};
Do a google search for sails.js many to many through to also help you find what your looking for.
I have models in Sails as follows:
User Model
attributes: {
firstName: {
type: 'string',
required: true
},
lastName: {
type: 'string',
required: true
},
company: {
model: 'Company'
}
}
Company
name: {
type: 'string',
required: true,
unique: true
},
description: {
type: 'string',
required: true
}
In HQL queries, for getting a user working in a specific company, we use something like this:
Select * from User where company.name=?
How can I achieve same in Sails, Right now there are two queries which I am running, one to get User and then another to get company for that user. Is there any way both can be combined in one?
and one more thing, how does waterline deal with scenarios where in we need to get something out of a foreign key directly i.e. If I get user data, then can I get company details by just calling:
User.findOne({id:1}, function(err, res){
//Res contains user data with just companyId now,
//how can i get whole company object here
var companyName = res.company.name; //This is not working currently
})
Try something like this:
User.findOne({id: 1})
.populate('company')
.exec(function(err, user) {
if(err){
//handle errors
}else{
//do stuff
}
});
This should get the values from the association (foreign key).