Sequelize - scope with include - node.js

I'm using Sequelize in my Node.js/Express app with the notion of scopes (http://docs.sequelizejs.com/en/latest/docs/scopes/).
I would like to have two scopes on a Model. Each of these scope are based on a join of another table (see the two scopes inLocation & withExpertises):
const Agency = sequelize.define("agency", {
id: { type: DataTypes.INTEGER, primaryKey: true },
name: DataTypes.STRING,
address: DataTypes.STRING,
size: DataTypes.INTEGER,
description: DataTypes.TEXT,
logoFileName: { type: DataTypes.STRING, field: "logo_file_name" },
externalScore: { type: DataTypes.FLOAT, field: "external_score" }
}, {
classMethods: {
associate(models) {
Agency.belongsToMany(models.location, {
through: "locationsAgency",
foreignKey: "location_id"
});
Agency.hasOne(models.locationsAgency);
Agency.hasMany(models.service);
}
},
scopes: {
inLocation(locationId) {
return {
include: [{
model: locationsAgency, where: { locationId: locationId }
}]
};
},
withExpertises(expertiseIds) {
return {
include: [{
model: service, where: { expertiseId: expertiseIds }
}]
};
},
orderByRelevance: {
order: '"externalScore" DESC'
}
}
});
When I'm doing the following call:
models.agency.scope({ method: ["inLocation", 9260] },
{ method: ["withExpertises", 79] },
"orderByRelevance")
.findAll({ limit: 10 })
I have this SQL generated:
SELECT "agency".*,
"services"."id" AS "services.id",
"services"."expertise_id" AS "services.expertiseId"
FROM (
SELECT "agency".*,
"locationsAgency"."id" AS "locationsAgency.id",
"locationsAgency"."location_id" AS "locationsAgency.locationId",
"locationsAgency"."agency_id" AS "locationsAgency.agencyId"
FROM "agencies" AS "agency"
INNER JOIN "locations_agencies" AS "locationsAgency" ON "agency"."id" = "locationsAgency"."agency_id"
AND "locationsAgency"."location_id" = 9260
WHERE (
SELECT "agency_id"
FROM "services" AS "service"
WHERE ("service"."agency_id" = "agency"."id" AND "service"."expertise_id" = 79)
LIMIT 1
)
IS NOT NULL LIMIT 10
) AS "agency"
INNER JOIN "services" AS "services" ON "agency"."id" = "services"."agency_id"
AND "services"."expertise_id" = 79
ORDER BY "externalScore" DESC
As you can see, the scope are nested in the FROM-clause, which give me a awful SQL and then the limit statement is not a the good place.
I expected to have this following SQL query:
SELECT "agency".*,
"services"."id" AS "services.id",
"services"."expertise_id" AS "services.expertiseId",
"services"."created_at" AS "services.created_at",
"services"."updated_at" AS "services.updated_at",
"locationsAgency"."id" AS "locationsAgency.id",
"locationsAgency"."location_id" AS "locationsAgency.locationId",
"locationsAgency"."agency_id" AS "locationsAgency.agencyId"
FROM "agencies" AS "agency"
INNER JOIN "locations_agencies" AS "locationsAgency" ON "agency"."id" = "locationsAgency"."agency_id"
INNER JOIN "services" AS "services" ON "agency"."id" = "services"."agency_id"
AND "locationsAgency"."location_id" = 9260
AND "services"."expertise_id" = 79
ORDER BY "external_score" DESC
LIMIT 10
Any ideas how to have this sql with the use of scopes?
Thanks!

Related

sequelize filtering for nested model

I've been working with sequelize in a couple of weeks, still trying to learn..
My Models are defined in models folder.
/models/endpoint.js
import Sequelize from 'sequelize';
import { sequelize } from '../data';
import Permission from './permission';
const { Model } = Sequelize;
class Endpoint extends Model { }
Endpoint.init({
endpoint: {
type: Sequelize.VIRTUAL(Sequelize.STRING, [ 'method', 'path' ]),
get() {
return `${this.get('method')} ${this.get('path')}`;
}
},
method: {
type: Sequelize.ENUM,
values: [ 'GET', 'POST', 'PUT', 'DELETE', 'PATCH' ],
allowNull: false
},
path: {
type: Sequelize.STRING,
allowNull: false
},
description: {
type: Sequelize.TEXT
}
}, {
sequelize,
modelName: 'endpoint'
});
Endpoint.Permissions = Endpoint.hasMany(Permission);
Permission.Endpoint = Permission.belongsTo(Endpoint, { as: 'permission' });
export default Endpoint;
/models/permission.js
import Sequelize from 'sequelize';
import { sequelize } from '../data';
const { Model } = Sequelize;
class Permission extends Model { }
Permission.init({
entity: {
type: Sequelize.ENUM,
values: [ '*', 'user', 'extension', 'queue', 'group' ],
defaultValue: '*'
},
role: {
type: Sequelize.STRING,
defaultValue: '*'
}
}, {
sequelize,
modelName: 'permission'
});
export default Permission;
found something in docs: Top level where with eagerly loaded models
User.findAll({
where: {
'$Instruments.name$': { [Op.iLike]: '%ooth%' }
},
include: [{
model: Tool,
as: 'Instruments'
}]
})
as following that, I made this
Endpoint.findAndCountAll({
where: {
path: { [Symbol(iLike)]: '%user%' },
'$permissions.role$': { [Symbol(eq)]: 'admin' }
},
include: [
{ model: permission, as: 'permissions' }
],
order: [],
offset: 0,
limit: 20
})
my code throws this error:
SequelizeDatabaseError: missing FROM-clause entry for table
"permissions"
and generated SQL query:
SELECT "endpoint".*,
"permissions"."id" AS "permissions.id",
"permissions"."entity" AS "permissions.entity",
"permissions"."role" AS "permissions.role",
"permissions"."createdAt" AS "permissions.createdAt",
"permissions"."updatedAt" AS "permissions.updatedAt",
"permissions"."endpointId" AS "permissions.endpointId",
"permissions"."permissionId" AS "permissions.permissionId"
FROM (
SELECT "endpoint"."id",
"endpoint"."method",
"endpoint"."path",
"endpoint"."description",
"endpoint"."createdAt",
"endpoint"."updatedAt"
FROM "endpoints" AS "endpoint"
WHERE "endpoint"."path" ilike '%user%'
AND "permissions"."role" = 'admin' limit 20 offset 0) AS "endpoint"
LEFT OUTER JOIN "permissions" AS "permissions"
ON "endpoint"."id" = "permissions"."endpointId";
Don't try to use limit and offset with hasMany associations and with findAndCountAll.
If you have 2 permissions in each endpoint (for instance you have 10 endpoints and set LIMIT to 10) then you'll get first 5 endpoints because in SQL query endpoints will be multiplied with permissions.
If you still want to filter parent records (endpoints) by some conditions on child records then I suppose you need to use sequelize.literal for that purpose.
If you want to filter parent and child records separately (and still use LIMIT and OFFSET) just move the permissions condition clause to association's where clause and also set separate:true in an association include to make LIMIT and OFFSET work correctly (this will make sequelize to execute separate SQL query to get permissions for each endpoint. This latter case will look like this:
Endpoint.findAndCountAll({
where: {
path: { [Symbol(iLike)]: '%user%' }
},
include: [
{
model: permission,
as: 'permissions',
separate: true,
where: {
role: { [Symbol(eq)]: 'admin' }
}
}
],
order: [],
offset: 0,
limit: 20
})

Nested associated data through Sequelize join tables

Using Sequelize, 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;
};
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?
I think you are overcomplicating this thinking you have a many to many to many..and i can see that the fields for your model for GameTeam do not match up with the foreign keys you have declared in your other models...
What do your database tables look like?
Am i correct in saying, that a game has many teams, and a team has many users... however a user can only be on one team at a time, and a team is only in one game at a time? (i am assuming the game/team join and the team/user join are simply temporary records in the join tables disappearing after the game is over etc)

Sequelize how to make a join request?

I'm trying to make joined queries with Sequelize.
That's my db :
What I need is to select all of my relations and get this kind of result:
[
{
id: 1,
State: true,
FK_User: {
id: 2,
Name: "my name"
},
FK_Team: {
id: 3,
Name: "team name"
}
},
...
]
But today I've got this result:
[
{
id: 1,
State: true,
FK_User: 2,
FK_Team: 3
},
...
]
For each of my relations, I've go to do another request to get datas ...
So I putted a look in this Stack and in the doc.
Then I made this code :
let User = this.instance.define("User", {
Name: {
type: this.libraries.orm.STRING,
allowNull: false
}
});
let Team = this.instance.define("Team", {
Name: {
type: this.libraries.orm.STRING,
allowNull: false
}
});
let Relation = this.instance.define("Relation", {
State: {
type: this.libraries.orm.BOOLEAN,
allowNull: false,
defaultValue: 0
}
});
Relation.hasOne(User, {as: "FK_User", foreignKey: "id"});
Relation.hasOne(Team, {as: "FK_Team", foreignKey: "id"});
With this code, I haven't got any relation between tables... So I added theses two lines. I don't understand why I need to make a two direction relation, because I don't need to access Relation From User and Team ...
User.belongsTo(Relation, {foreignKey: 'FK_User_id'});
Team.belongsTo(Relation, {foreignKey: 'FK_Team_id'});
When I do that, I've a FK_User_id in the User table and a FK_Team_id in the Team table ... I don't know how to make this simple relation and get all I need with my futur request and the include: [User, Team]} line.
User.hasOne(Relation);
Team.hasOne(Relation);
Relation.belongsTo(Team);
Relation.belongsTo(User);
This code seems to work.
I don't know why ...
Here your associations are setup correctly you can join it with include :
Relation.findAll({
where : {
state : true
}
include:[
{
model : User
},
{
model : Team
}
]
})

SequelizeJS, Is this a best way to create this JSON Result using this models

I want this result, but, is so complicated do to this in this way. There is a better way to create this result using Sequelize. Using the querys tools of sequelize to aggregate results from diferent tables, on JPA the only thing i do is annotate with join table and pass the columns ad invese columns values.
[
{
"id": 1,
"codemp": "999",
"nome": "A3 Infortech",
"limiteInstancias": "10",
"instancias": []
},
{
"id": 2,
"codemp": "92",
"nome": "Endovideo",
"limiteInstancias": "20",
"instancias": [
{
"id": 198211,
"ipLocal": "40.0.10.11",
"ipExterno": "187.33.230.106",
"hostname": "FATURAMENTO-PC",
"dataCriacao": "2019-07-01T21:40:29.000Z"
}
]
},
{
"id": 6,
"codemp": "103",
"nome": "SOS Otorrino",
"limiteInstancias": "999",
"instancias": [
{
"id": 199127,
"ipLocal": "192.168.11.101",
"ipExterno": "000.000.000.000",
"hostname": "Guiche3-PC",
"dataCriacao": "2019-07-01T21:40:32.000Z"
},
{
"id": 199164,
"ipLocal": "192.168.25.209",
"ipExterno": "000.000.000.000",
"hostname": "Consultorio06",
"dataCriacao": "2019-07-01T21:40:29.000Z"
}
]
},
{
"id": 15,
"codemp": "162",
"nome": "Clinica Vida e Saude",
"limiteInstancias": "10",
"instancias": [
{
"id": 199774,
"ipLocal": "192.168.56.1",
"ipExterno": "000.000.000.000",
"hostname": "ALEXANDRELEAL",
"dataCriacao": "2019-07-01T21:40:28.000Z"
}
]
}
]
I have this codes:
Empresa Model
module.exports = (sequelize, DataTypes) => {
const empresa = sequelize.define("empresa", {
id: {
type: DataTypes.BIGINT(20),
primaryKey: true,
field: "id"
},
codemp: {
type: DataTypes.INTEGER,
field: "codemp"
},
nome: {
type: DataTypes.STRING,
field: "nome"
},
limiteInstancias: {
type: DataTypes.INTEGER,
field: "limite_instancias"
}
}, {
timestamps: false,
freezeTableName: true,
tableName: "empresa"
});
empresa.associate = (db) => {
console.log(db);
empresa.hasMany(db.instanciaEmpresa, {foreignKey: "id_empresa"});
};
return empresa;
};
Instancia Model
module.exports = (sequelize, DataTypes) => {
const instancia = sequelize.define("instancia", {
id: {
type: DataTypes.BIGINT(20),
primaryKey: true,
field: "id"
},
ipLocal: {
type: DataTypes.STRING,
field: "ip_local"
},
ipExterno: {
type: DataTypes.STRING,
field: "ip_externo"
},
hostname: {
type: DataTypes.STRING,
field: "hostname"
},
dataCriacao: {
type: DataTypes.DATE,
field: "data_criacao"
},
}, {
timestamps: false,
freezeTableName: true,
tableName: "instancia"
});
instancia.associate = (db) => {
console.log(db);
instancia.belongsTo(db.empresa, {foreignKey: "id_instancia"});
};
return instancia;
};
InstanciaEmpresa Model
module.exports = (sequelize, DataTypes) => {
const instanciaEmpresa = sequelize.define("instancia_empresa", {
idEmpresa: {
type: DataTypes.BIGINT(20),
primaryKey: true,
field: "id_empresa"
},
idInstancia: {
type: DataTypes.BIGINT(20),
primaryKey: true,
field: "id_instancia"
},
}, {
timestamps: false,
freezeTableName: true,
tableName: "instancia_empresa"
});
return instanciaEmpresa;
};
My Database diagram.
A picture of my database diagram
The code of my response
const db = require("../config/db.config");
const empresa = db.empresa;
const instancia = db.instancia;
const instanciaEmpresa = db.instanciaEmpresa;
const empressaResult = [];
module.exports = {
async getAll(req, res) {
return res.send(await getAllEmpresa());
}
};
async function getAllEmpresa() {
//Recover all companies from the table
let empresaList = await empresa.findAll({raw: true});
//I browse the array of companies to retrieve the instances associated with the company
for(let i = 0; i < empresaList.length; i++){
//Create the atribute Instancias[]
empresaList[i].instancias = [];
//I retrieve the list of associated instances in the InstanciaEmpresa table
let instanciasEmpresa = await instanciaEmpresa.findAll({where: {"id_empresa": empresaList[i].id}, raw: true});
//Verify if existes any item of InstanciaEmpresa
if(instanciasEmpresa.length > 0){
//If there is a run through list of instances
for(let j = 0; j < instanciasEmpresa.length; j++){
//I retrieve the Instancia in the database and add it to the company Instancias list
let inst = await instancia.findByPk(instanciasEmpresa[j].idInstancia, {raw: true});
empresaList[i].instancias.push(inst);
}
}
//I add the company with the instances in a result list;
empressaResult.push(empresaList[i]);
}
return empressaResult;
}
You can use include option to operate join on your tables.
Then your code would look like,
const empresaList = await empresa.findAll({
raw: true,
include: [
{
model: instancias,
required: false, // left join, `true` means inner join.
}
]
});
As you can see, you can pass array of { model, required } into include option.
You can set required to true if you want to operate inner join else it would operate left join.
--- ADDED ---
SequelizeEagerLoadingError: instancia is not associated to empresa means you're not calling associate function on db initialization.
You can write helper function like below in your db.js.
addAssociations(name) {
if (this[name].associate) {
this[name].associate(this);
}
}
}
and use it like
/*
* this.models = [
* {
* name: 'instancias',
* model: instancias,
* },
* ... and many more
* ]
*/
this.models.forEach((value) => {
this.addAssociations(value.name);
});

Sequelize include same association twice with different criteria

Say I have a table suppliers, which is associated to tags through another table suppliers_tags. So my model looks like this:
const Supplier = Sequelize.define('suppliers', {
id: { type: Sequelize.INTEGER, primaryKey: true },
name: Sequelize.STRING,
});
const SupplierTag = Sequelize.define('suppliers_tags', {
id: { type: Sequelize.INTEGER, primaryKey: true },
supplier_id: { type: Sequelize.INTEGER, references: { model: 'suppliers', key: 'id' } },
tag_id: { type: Sequelize.INTEGER, references: { model: 'suppliers', key: 'id' } },
});
const Tag = Sequelize.define('tags', {
id: { type: Sequelize.INTEGER, primaryKey: true },
name: Sequelize.STRING,
type: Sequelize.ENUM('food', 'drink'),
});
The associations look like this:
Supplier.belongsToMany(Tag, { as: 'tags', through: 'suppliers_tags', foreignKey: 'supplier_id' });
Tag.belongsToMany(Supplier, { as: 'supplierTag', through: 'suppliers_tags', foreignKey: 'tag_id' });
Let's say I have the following data in the database:
suppliers:
id name
1 Supplier1
2 Supplier2
3 Supplier3
tags:
id name type
1 Food1 food
2 Vegan food
3 Vegan drink
4 Food2 food
(I purposely named two tags the same, as for this application, it is important that tags with different types can have the same name.)
suppliers_tags
id supplier_id tag_id
1 1 1
2 1 3
3 1 2
4 2 1
5 2 4
6 3 2
Now I can do the following query:
Supplier.findAll({
include: [
{
model: Tag,
as: 'tags',
where: {
[Op.and]: [
{ type: 'food' },
{ name: 'Vegan' },
],
},
},
],
});
This returns Supplier1 and Supplier3, correctly joining on suppliers_tags and tags and filtering the tags table to include ones of type 'food' and name 'Vegan'.
Now, what if I want to search for suppliers where both the following conditions hold:
The supplier has an associated tag which is of type food and name Food1
The supplier has an associated tag which is of type drink and name Vegan
Naively (?), I tried the following:
Supplier.findAll({
include: [
{
model: Tag,
as: 'tags_food',
where: {
[Op.and]: [
{ type: 'food' },
{ name: 'Food1' },
],
},
},
{
model: Tag,
as: 'tags_drink',
where: {
[Op.and]: [
{ type: 'drink' },
{ name: 'Vegan' },
],
},
},
],
});
This tries to join the tags table twice, but neglecting to add aliases, resulting in the following error:
SequelizeEagerLoadingError: tags is associated to suppliers multiple times. To identify the correct association, you must use the 'as' keyword to specify the alias of the association you want to include.
Right, so it seems like the as options on the include didn't have the desired effect. What if I modify the associations to be the following:
Supplier.belongsToMany(Tag, { as: 'tags_drink', through: 'suppliers_tags', foreignKey: 'supplier_id' });
Supplier.belongsToMany(Tag, { as: 'tags_food', through: 'suppliers_tags', foreignKey: 'supplier_id' });
Tag.belongsToMany(Supplier, { as: 'supplierTag', through: 'suppliers_tags', foreignKey: 'tag_id' });
Now, if I run the same findAll query, it generates the following SQL:
SELECT
"suppliers".*
,"tags_food"."id" AS "tags_food.id"
,"tags_food"."name" AS "tags_food.name"
,"tags_food"."type" AS "tags_food.type"
,"tags_food->suppliers_tags"."supplier_id" AS "tags_food.suppliers_tags.supplier_id"
,"tags_food->suppliers_tags"."tag_id" AS "tags_food.suppliers_tags.tag_id"
,"tags_drink"."id" AS "tags_drink.id"
,"tags_drink"."name" AS "tags_drink.name"
,"tags_drink"."type" AS "tags_drink.type"
,"tags_drink->suppliers_tags"."supplier_id" AS "tags_drink.suppliers_tags.supplier_id"
,"tags_drink->suppliers_tags"."tag_id" AS "tags_drink.suppliers_tags.tag_id"
FROM (
SELECT
"suppliers"."id"
,"suppliers"."name"
FROM "suppliers" AS "suppliers"
WHERE (
SELECT "suppliers_tags"."supplier_id"
FROM "suppliers_tags" AS "suppliers_tags"
INNER JOIN "tags" AS "tag" ON "suppliers_tags"."tagId" = "tag"."id"
AND ("tag"."type" = 'food' AND "tag"."name" = 'Food1')
WHERE ("suppliers"."id" = "suppliers_tags"."supplier_id")
LIMIT 1
) IS NOT NULL
AND (
SELECT "suppliers_tags"."supplier_id"
FROM "suppliers_tags" AS "suppliers_tags"
INNER JOIN "tags" AS "tag" ON "suppliers_tags"."tag_id" = "tag"."id"
AND ("tag"."type" = 'drink' AND "tag"."name" = 'Vegan')
WHERE ("suppliers"."id" = "suppliers_tags"."supplier_id")
LIMIT 1
) IS NOT NULL
) AS "suppliers"
INNER JOIN (
"suppliers_tags" AS "tags_food->suppliers_tags"
INNER JOIN "tags" AS "tags_food"
ON "tags_food"."id" = "tags_food->suppliers_tags"."tagId"
) ON "suppliers"."id" = "tags_food->suppliers_tags"."supplier_id"
AND ("tags_food"."type" = 'food' AND "tags_food"."name" = 'Food1')
INNER JOIN (
"suppliers_tags" AS "tags_drink->suppliers_tags"
INNER JOIN "tags" AS "tags_drink"
ON "tags_drink"."id" = "tags_drink->suppliers_tags"."tag_id"
) ON "suppliers"."id" = "tags_drink->suppliers_tags"."supplier_id"
AND ("tags_drink"."type" = 'drink' AND "tags_drink"."name" = 'Vegan')
ORDER BY "suppliers"."id"
This is correct and what I need, except for one error: the foreign key tag_id has been switched to tagId (?!) in the case of the tags_food join. This then of course fails with the following error:
SequelizeDatabaseError: column tags_food->suppliers_tags.tagId does not exist
Notice that the correct foreign key tag_id was generated in the join on tags_drink by Sequelize.
If I switch around the relation definitions, i.e.:
Supplier.belongsToMany(Tag, { as: 'tags_food', through: 'suppliers_tags', foreignKey: 'supplier_id' });
Supplier.belongsToMany(Tag, { as: 'tags_drink', through: 'suppliers_tags', foreignKey: 'supplier_id' });
Tag.belongsToMany(Supplier, { as: 'supplierTag', through: 'suppliers_tags', foreignKey: 'tag_id' });
Then tag_id is produced successfully for the tags_food join and the weird tagId is produced for the tags_drink join.
Can I put this down to another basic flaw with Sequelize, or:
Is there a defined way of joining the same relation twice on different criteria in Sequelize?

Resources