Sequelize Getting model object from relations - node.js

When I define a relation, I specify what model object it is related to:
models.Guest.belongsTo(models.Member, {foreignKey: 'guestId', as: 'guest'});
Now to use this relationship in a query, I need to specify the model object again:
Guest.findAll({
include: models.Member,
as: 'guest'
});
In my setup, all the relationships are defined in a single file, while individual models are in files by themselves. The setup defines the models object, which has all of them - and the thing is available to application logic.
What I would like is define some of the more complex queries directly in the Guest model object, to abstract them away from the application. To do this, I'd like to find the models.Member object - retrieving it somewhere out of the relationship:
Guest.findMembers = function(){
return this.findAll({
include: <that model that guest is related to>,
as: 'guest'
});
}
Please imagine that the example is complex enough that it warrants solving. Lots more other joins and where statements, for instance. Or data processing after the fact.
I can pass the models object into the setup of each individual object, so its siblings can be accessed. Is that my only option?

You can just also add an include in your include mode also
Guest.findMembers = function(){
return this.findAll({
include: [{
model: <that model that guest is related to>,
as: 'guest',
include: [{
model: <that model that guest is related this included model>,
as: 'setTheAlias',
}]
}]
});
}

Related

Does defining relationships in models using sequilizejs partially or completely remove the need for using include parameter?

What is the advantage of defining relationships in sequilizejs on a given modal , is it just for the purpose of more succinct queries ? I do see an example such as (Found HERE) :-
User.hasMany(Picture)
User.belongsTo(Picture, { as: 'ProfilePicture', constraints: false })
user.getPictures() // gets you all pictures
user.getProfilePicture() // gets you only the profile picture
User.findAll({
where: ...,
include: [
{ model: Picture }, // load all pictures
{ model: Picture, as: 'ProfilePicture' }, // load the profile picture. Notice that the spelling must be the exact same as the one in the association
]
})
Does having a relationship defined avaoid the usage of too many queries with the include param ? I.E. does the below :-
user.getPictures() // gets you all pictures
user.getProfilePicture() // gets you only the profile picture
Do away with the more verbose :-
User.findAll({
where: ...,
include: [
{ model: Picture }, // load all pictures
{ model: Picture, as: 'ProfilePicture' }, // load the profile picture. Notice that the spelling must be the exact same as the one in the association
]
})
I am using sequilizejs in an already existing project so the models were not created by me and hence an trying to understand the advantage of having relationships such as belongsTo and hasOne being defined in a model. Would appreciate any insight into the same.
P.S. I do see a more detailed documentation HERE
Also , as a secondary question what are the other advantages of using relationships in a db ?
Defining the relationships is actually required to be able to use include in the queries.
By using include, Sequelize will create a JOIN query, so you can get data from multiple collections which are linked by relationships without the need of a second query.
When using belongsTo, hasOne, hasMany etc. these methods are creating aditional columns in the table to create the relationships between the tables.

Sequelize hasOne with BelongsToMany

I have to models DistrictArea and Order which Order must have a DistrictArea instance in it.
so I have done this so far:
Order.hasOne(models.DistricArea)
and in migration file I used this.
queryInterface.addColumn('Orders', 'district_area_id', {
type: Sequelize.INTEGER,
references: {
model: 'DistricAreas',
key: 'id'
}
})
and I have two questions. If we use associated methods in the model , we have to explicitly define these field to our models?
district_area_id: DataTypes.INTEGER
and do I need to define belongsTo in another model (DistricArea)?
the second question is I didn't define any field in DistricArea that associated with Orders. but when I want to use query DistricArea.findAll() it comes with this error:
"column "OrderId" does not exist"
so this is the goal I am trying to achieve:
I need to tell we have many orders that, every order has a
DistricArea.
Yes you still need to define the fields in your model
from what you described above it should be belongsTo instead of hasOne for order model , and hasMany in DistricArea model and then define your sourceKey using your district_area_id field.
Order.belongsTo(models.DistricArea,{foreignKey: 'district_area_id'})
DistricArea.hasMany(models.Order, {foreignKey: 'district_area_id', sourceKey: 'id'})
after this , in DistricArea.findAll() you would get all DistricArea and then inside you will have a Order array which contain every order for each DistricArea, or in a Order.findAll() call you will see all orders with a field district_area_id

Is it possible somehow to set defaul joins/relations for model in Sequelize?

So I have model Alarms which is associated with Site model and others... Is it possible somehow set that by default when are required Alarm.findAll().then() I didn’t need to specify which associated models I need? It is necessary because Alarms table are using in many different situations and some different apps but in my case I need only entries which has site.
Or may be somehow I can add default joins to the model?
Usually when I encounter situations like this, I would just make a module which returns a promise of the query (with joins). So, for example you could make an alarm_util module
exports.getAlarm = function() {
return Alarms.findAll({
include: [{
model: Site,
include: [{
model: OtherModel
}]
}]
});
};
module.exports = exports;
And use it anywhere in your code like
alarm_util.getAlarm().then(alarm => {
// The rest of your logic here...
});

Sequelize's dynamic set/get methods for assoctiations

The only reference in Sequelize's documentation that I could find about this is at Relations / Associations.
Apparently, if you have something like:
Person.hasOne(Person, {as: 'Father', foreignKey: 'DadId'})
then you can do something like:
Person.setFather();
Person.getFather();
As far as I understand, these set and get methods are created dynamically for associations.
However, I have tried something similar and it does not work:
Person.hasOne(Father, { foreignKey: 'father_id' });
var aPerson = Person.build({
name: 'Mike'
});
aPerson.setFather({ id: 1 });
I realized that the { id: 1 } bit may not be correct, but the problem is that I get a message saying that the setFather function is undefined.
Basically, my question is: how can I attach associations to an already-created instance of some model in Sequelize?
Do you have a specific model for fathers? The associations uses the model name, which means to just importing the user model as Father is not going to work:
var Person = sequelize.define('person')
var Father = person;
Person.hasOne(Father) // adds setPerson
Person.hasOne(Person, { as: 'father' }) // adds setFather
I recently updated the API docs for associations - hopefully it should be a bit clearer which functions are added and what parameters they take http://docs.sequelizejs.com/en/latest/api/associations/has-one/

Join across multiple junction tables with Sequelize

I have a database with three primary tables: users, teams, and folders joined by two junction tables, users_teams and teams_folders. There is a many-to-many relationship between users and teams and between teams and folders (a user can be on more than one team and teams can own more than one folder).
Sequelize does a wonderful job of managing the user-teams and teams-folder relationship, but I can find no way to establish a relationship between users and folders.
Is there any way to join across two junction tables without resorting to raw SQL?
There seems to be no way to accomplish this elegantly or in a reasonable number of steps. I have tried methods like user.getFolders(), Folder.findAll({ include: [User] }), but Sequelize doesn't seem to be able to understand a three level hierarchy.
Assuming the following relations:
User.belongsToMany(Team, { through: 'users_teams'});
Team.belongsToMany(User, { through: 'users_teams'});
Folder.belongsToMany(Team, { through: 'teams_folders'});
Team.belongsToMany(Folder, { through: 'teams_folders'});
You should be able to load everything in one go using nested includes:
User.findAll({
include: [
{
model: Team,
include: [
Folder
]
}
]
});
You seem to be on the right track already with the example you have given in your post :). The only thing you need to change is instead of passing the User model directly in include, you pass an object with a model property and a further nested include property
Pay attention to following:
Define relations in both directions
Check you have foreignKey, otherKey in correct order
User.belongsToMany(Team, {
through: 'users_teams',
foreignKey: 'user_id',
otherKey: 'team_id'
});
Team.belongsToMany(User, {
through: 'users_teams',
foreignKey: 'team_id',
otherKey: 'user_id'
});

Resources