question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

many-to-many-to-many query

See original GitHub issue

Issue Description

I’m trying to get an output like this:

[{
  "Id": 1,
  "Name": "Game 1",
  "Teams": [{
    "Id": 1,
    "Name": "Team 1",
    "Users": [{
      "Id": 1,
      "UserName": "User 1"
    }]
  }]
}, {
  "Id": 2,
  "Name": "Game 2",
  "Teams": [{
    "Id": 1,
    "Name": "Team 1",
    "Users": [{
      "Id": 2,
      "UserName": "User 2"
    }]
  }]
}]

Note that Team 1 has 2 different users, but that’s only because they’re set up that way per game… so a user isn’t tied directly to a team, but rather through a team game constraint. Basically, my Game HasMany Teams, and my Game/Team HasMany Users… a many-to-many-to-many relationship. I was trying to follow this thread, but it seems like what they’re doing there doesn’t actually work, as I tried doing this:

// models/Game.js
module.exports = (sequelize, types) => {
  const GameModel = sequelize.define('Game', {
    Id: {
      type: types.INTEGER,
      primaryKey: true,
      autoIncrement: true
    },
    Name: {
      type: types.STRING,
      allowNull: false
    }
  });

  GameModel.associate = (models) => {
    GameModel.belongsToMany(models.Team, {
      as: 'Teams',
      foreignKey: 'GameId',
      through: models.GameTeam
    });
  };

  return GameModel;
};

// models/Team.js
module.exports = (sequelize, types) => {
  const TeamModel = sequelize.define('Team', {
    Id: {
      type: types.INTEGER,
      primaryKey: true,
      autoIncrement: true
    },
    Name: {
      type: types.STRING,
      allowNull: false
    }
  });

  TeamModel.associate = (models) => {
    TeamModel.belongsToMany(models.Game, {
      as: 'Games',
      foreignKey: 'TeamId',
      through: models.GameTeam
    });
  };

  return TeamModel;
};

// models/User.js
module.exports = (sequelize, types) => {
  const UserModel = sequelize.define('User', {
    Id: {
      type: types.INTEGER,
      primaryKey: true,
      autoIncrement: true
    },
    UserName: {
      type: types.STRING,
      allowNull: false
    }
  });

  return UserModel;
};

// models/GameTeam.js
module.exports = (sequelize, types) => {
  const GameTeamModel = sequelize.define('GameTeam', {
    Id: {
      type: types.INTEGER,
      primaryKey: true,
      autoIncrement: true
    }
  });

  GameTeamModel.associate = (models) => {
    GameTeamModel.belongsToMany(models.User, {
      as: 'Users',
      through: 'GameTeamUser'
    });
  };

  return GameTeamModel;
};

// models/GameTeamUser.js
module.exports = (sequelize, types) => {
  const GameTeamUserModel = sequelize.define('GameTeamUser', {
    Id: {
      type: types.INTEGER,
      primaryKey: true,
      autoIncrement: true
    }
  });
  return GameTeamUserModel;
};

The above models create the tables just fine, with what appears to be the appropriate columns. I then do some inserts and try to use a findAll on the Game model like this:

GameModel.findAll({
  include: [{
    association: GameModel.associations.Teams,
    include: [{
      association: GameTeamModel.associations.Users,
      through: {
        attributes: []
      }
    }],
    through: {
      attributes: []
    }
  }]
});

The query starts to go wrong at the 2nd include with the association of the Users. Because I’m trying to nest the users inside of the teams, I figured the join would attempt to use the unique ID on the through table (GameTeams.Id), but instead, the query ends up using this:

LEFT OUTER JOIN `GameTeamUser` AS `Teams->Users->GameTeamUser` ON `Teams`.`Id` = `Teams->Users->GameTeamUser`.`GameTeamId`

I figured the ON would be GameTeams.Id = Teams->Users->GameTeamuser.GameTeamId, but I don’t know why it’s not, and how to adjust it… I’ve tried using a custom on in my include (per the docs), but it seems to be ignored completely. Anyone have any advice? Or possibly a better way of structuring this, so it works the way I want it to?

StackOverflow / Slack attempts

StackOverflow thread.

Additional context

package.json deps (using SQLite as the backing DB):

"dependencies": {
    "cookie-parser": "~1.4.4",
    "debug": "~2.6.9",
    "express": "~4.16.1",
    "ip": "^1.1.5",
    "morgan": "~1.9.1",
    "sequelize": "^5.21.3",
    "sequelize-cli": "^5.5.1",
    "sqlite3": "^4.1.1"
  }

Is this issue dialect-specific?

[ ] No. This issue is relevant to Sequelize as a whole. [ ] Yes. This issue only applies to the following dialect(s): XXX, YYY, ZZZ [✔] I don’t know.

Would you be willing to resolve this issue by submitting a Pull Request?

[ ] Yes, I have the time and I know how to start. [✔] Yes, I have the time but I don’t know how to start, I would need guidance. [ ] No, I don’t have the time, although I believe I could do it if I had the time… [ ] No, I don’t have the time and I wouldn’t even know how to start.

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Comments:19 (7 by maintainers)

github_iconTop GitHub Comments

3reactions
jedwards1211commented, May 6, 2020

@papb although your new many-to-many-to-many example in the docs is a solution, it’s not ideal because it requires two join tables (GameTeam and PlayerGameTeam), whereas using raw SQL, it’s possible to have a single join table with all three foreign keys. Imagine if someone has a pre-existing database with a three-way join table and tries to start using Sequelize on it…they would have to keep using raw SQL. I think an API like this would be a welcome addition:

sequelize.joinThrough(PlayerGameTeam, {
  playerId: Player,
  teamId: Team,
  gameId: Game,
})
1reaction
incutonezcommented, Jan 20, 2020

I’m not entirely convinced it has changed… it started out with me asking how to get that output, and I still haven’t learned how to do that naturally through the ORM without doing manual manipulation… unless that is the only way of doing it. It really sounds like this is not possible in the current version though, so I guess I’ll just have to chalk it up to that’s what I get for using this ORM.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Joining three tables with Hibernate annotations - Stack Overflow
ManyToManyToMany - Joining three tables with Hibernate annotations · Ask Question. Asked 5 years, 10 months ago.
Read more >
Joining three tables with Hibernate annotations-Hibernate
Coding example for the question ManyToManyToMany - Joining three tables with Hibernate annotations-Hibernate.
Read more >
many-to-many-to-many relationships - MSDN - Microsoft
If it actually answered your question, please remember to "Mark as Answer". This will help other people find answers to their problems more...
Read more >
EntityManagerFactoryImpl.java example - Javatips.net
This class describes the usage of EntityManagerFactoryImpl.java.
Read more >
Relations | Objection.js
... static get relationMappings() { // Solution 1: // // relationMappings getter is accessed lazily when you execute // your first query that...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found