Search code examples
databasenode.jsmany-to-manysequelize.jseager-loading

Sequelize.js many to many eager loading


I have 2 models: User and Team

There are multiple kinds of users (in this case Mentors and Moderators) which are differentiated using an attribute in the User model(). The associations between User and Team are as below:

User.hasMany(models.Team, {as: 'Mentors', through: models.TeamMentor, foreignKey: 'mentorId'});
User.hasMany(models.Team, {as: 'Moderators', through: models.TeamModerator, foreignKey: 'moderatorId'});


Team.hasMany(models.User, {through: models.TeamMentor, foreignKey: 'teamId'});
Team.hasMany(models.User, {through: models.TeamModerator, foreignKey: 'teamId'});

Now I am trying to get the details of the team along with separate objects for all the mentors and moderators that are assigned to the teams. I came to know about the getters and setters for many to many relationships from the documentation but I am not sure how to use the method since there are two different kinds of associations between two models here:

  1. Team - Mentor (User)
  2. Team - Moderator (User)

How to correctly query for a team's details in this case?

PS: TeamMentor and TeamModerator are empty models to help the many to many joins


Solution

  • I'm assuming you have N:M relationships between all those models? Like this:

    User ---mentors many---> Teams
    User --moderates many--> Teams
    
    Team --is moderated by many--> Users
    Team --is mentored by many---> Users
    

    If so, you might want to use the as option not only in the User.hasMany(Team) that you already have, but also in the Team.hasMany(User):

    Team.hasMany(models.User, {as: 'mentors', through: models.TeamMentor, foreignKey: 'teamId'});
    Team.hasMany(models.User, {as: 'moderators', through: models.TeamModerator, foreignKey: 'teamId'});
    

    I think you can then eager load some or all of them during a query like so:

    Team.findAll({
      where: {...},
      include: [
        {model: User, as: `moderators` },
        {model: User, as: `mentors` }
    
        // or just use this to include everything:
        // include: [{all:true}]
      ]
    }).then(function(teams) {
      console.log(JSON.stringify(teams));
      // oneTeam.moderators: [array of User]
      // oneTeam.mentors: [array of User]
    });
    

    (side note: I think your User objects currently have a 'getMentors()' method, and a 'mentors' property, that is an array of Team. Same for the 'moderators' property. Perhaps renaming that to 'mentoredTeams' and 'moderatedTeams' would make it clearer?)