How to reference two tables using hasOne with sequelize.js - javascript

Considering these 3 models generated by sequelize-auto:
sequelize.define('users', {
id: {
type: DataTypes.INTEGER,
allowNull: false,
primaryKey: true,
autoIncrement: true
},
first: {
type: DataTypes.STRING,
allowNull: true
},
last: {
type: DataTypes.STRING,
allowNull: true
}
}, {
tableName: 'users',
underscored: true,
timestamps: false
});
sequelize.define('groups', {
id: {
type: DataTypes.INTEGER,
allowNull: false,
primaryKey: true,
autoIncrement: true
},
parent_id: {
type: DataTypes.INTEGER,
allowNull: true,
references: {
model: 'groups',
key: 'id'
}
}
}, {
tableName: 'groups',
underscored: true,
timestamps: false
});
sequelize.define('user_groups', {
group_id: {
type: DataTypes.INTEGER,
allowNull: false,
primaryKey: true,
references: {
model: 'groups',
key: 'id'
}
},
user_id: {
type: DataTypes.INTEGER,
allowNull: false,
references: {
model: 'users',
key: 'id'
}
}
}, {
tableName: 'user_groups',
underscored: true,
timestamps: false
});
I was expecting hasOne statements to be generated but I had to specify them like so:
user_groups.hasOne(orm.models.users, { foreignKey: "id" });
user_groups.hasOne(orm.models.groups, { foreignKey: "id" });
Also consider the following data in tables:
users (id, first, last):
1, John, Doe
group (id, parent_id):
1, NULL
2, NULL
3, NULL
4, NULL
user_groups (group_id, user_id):
1, 1
4, 1
Doing this query:
sequelize.findAll("user_groups", {
attributes: ["*"],
raw: true,
include: [{
model: models.users,
}]
});
I get the following results:
[ { group_id: 4,
user_id: 1,
'user.id': null,
'user.first': null,
'user.last': null },
{ group_id: 1,
user_id: 1,
'user.id': 1,
'user.first': 'John',
'user.last': 'Doe' } ]
This clearly shows that sequelize is using group_id for the user_id relation.
How can I specify a relation that links the user_groups relations to their respective tables in order to be able to associate a user to many groups?
I am also very curious as how the "references" key in the models definition is supposed to work as the documentation is inexistant on that.

I was able to get the referenced data using these associations:
groups.hasMany(orm.models.user_groups);
user_groups.belongsTo(orm.models.groups, {foreignKey: "group_id", as: "group"});
users.hasMany(orm.models.user_groups);
user_groups.belongsTo(orm.models.users, {foreignKey: "user_id", as: "user"});
And the following query:
sequelize.findAll("user_groups", {
attributes: ["*"],
raw: true,
include: [
{ model: users, foreignKey: "user_id", as: "user", attributes: ["first", "last"] }
]
});
With the expected results:
[ { group_id: 4,
user_id: 1,
'user.first': 'John',
'user.last': 'Doe' },
{ group_id: 1,
user_id: 1,
'user.first': 'John',
'user.last': 'Doe' } ]

Related

Nodejs Sequelize belongsToMany not working

I dont know why I am getting this error error: SequelizeDatabaseError: Unknown column 'student-> section ->grademapping.studentId' in 'field list' I just follow this documentation on how to use belongsToMany did i miss something on the documentation? https://sebhastian.com/sequelize-belongstomany/
let student = await Database.getConnection().models.Student;
let grademapping = await Database.getConnection().models.Grademapping;
let section = await Database.getConnection().models.Section;
let taxtypemapping = await student.findAll({
attributes:['_id','FirstName','LastName', 'section.Description'],
include:[
{ model: section, attribute:[] }
],
raw: true
})
the models
class taxtypedetails extends Sequelize.Model {}
Section.init(
{
_id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true,
},
Description: {
type: Sequelize.STRING,
},
},
{ sequelize, tableName: 'section', timestamps: false },
)
class Student extends Sequelize.Model {}
Student.init(
{
_id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true,
},
FirstName: {
type: Sequelize.STRING,
},
LastName: {
type: Sequelize.STRING,
},
},
{ sequelize, tableName: 'student', timestamps: false },
)
class Grademapping extends Sequelize.Model {}
Grademapping.init(
{
_id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true,
},
sectionID: {
type: Sequelize.INTEGER,
references: {
model: 'section',
key: '_id',
},
},
studentID: {
type: Sequelize.INTEGER,
references: {
model: 'student',
key: '_id',
},
}
},
{ sequelize, tableName: 'grademapping', timestamps: false },
)
student.belongsToMany(section,{through: grademapping})
section.belongsToMany(student,{through: grademapping})

Sequelize - Include based on specific attribute

I have a model defined as follows:
sequelize.define('game', {
id: {
type: type.INTEGER,
primaryKey: true,
autoIncrement: true,
},
leaderId: {
type: type.INTEGER,
allowNull: false,
references: {
model: 'users',
key: 'id',
},
},
playerId: {
type: type.INTEGER,
allowNull: false,
references: {
model: 'users',
key: 'id',
},
},
status: {
type: type.STRING,
defaultValue: 'running',
},
notes: {
type: type.TEXT,
},
});
I'm trying to use Sequelize to load all game object and include the User with the id equal to the playerId field.
The problem is I have two attributes (leaderId, playerId) which reference the User model so using include as follows does not work:
Game.findAll({
where: conditions,
include: [{ model: User }],
})
Is there a way to specify which attribute the include command should use?
const game = sequelize.define('game', {
id: {
type: type.INTEGER,
primaryKey: true,
autoIncrement: true,
},
leaderId: {
type: type.INTEGER,
allowNull: false,
references: {
model: 'users',
key: 'id',
},
},
playerId: {
type: type.INTEGER,
allowNull: false,
references: {
model: 'users',
key: 'id',
},
as:'player'
},
status: {
type: type.STRING,
defaultValue: 'running',
},
notes: {
type: type.TEXT,
},
});
game.associate = function (models) {
game.belongsTo(models.user, {
foreignKey: "playerId",
as: "player",
});
game.belongsTo(models.user, {
foreignKey: "leaderId",
as: "leader",
});
};
Game.findAll({
where: conditions,
include: ['player'],
})
Or
Game.findAll({
where: conditions,
include: [{model: User, as: 'player' }],
})
Or
Game.findAll({
where: conditions,
include: [{model: User, as: 'player', foreignKey: 'playerId' }],
})
https://github.com/nkhs/node-sequelize

Get data that contains all the ids of the array

I'm trying to make a filter songs, i have an array of genres ids that i retrieve from the client, i do this for get all the audios from one id:
Audio.findAll({
include: [{
model: db.Genres,
as: "genres",
where: {
id: {
[Op.and]: [1]
}
},
}]
})
But i need to get all audios that contains all the ids from array of genres/moods, also want filter audios by genres ids and moods ids, but i don't know how to make it, any idea? (One song can have many genres/moods)
Song Model
const Audio = sequelize.define('Audio', {
id: {
autoIncrement: true,
type: DataTypes.INTEGER(30),
allowNull: false,
primaryKey: true
},
name: {
type: DataTypes.STRING(255),
allowNull: false
},
})
Audio.associate = function(models) {
Audio.belongsToMany(models.Genres, {through: 'AudioGenres', foreignKey: 'id_audio', as: 'genres'})
Audio.belongsToMany(models.Moods, {through: 'AudioMoods', foreignKey: 'id_audio', as: 'moods'})
}
AudioGenreModel
const AudioGenres = sequelize.define('AudioGenres', {
id_audio: {
type: DataTypes.INTEGER(11),
allowNull: false,
primaryKey: true,
references: {
model: 'Audio',
key: 'id'
}
},
id_genre: {
type: DataTypes.INTEGER(11),
allowNull: false,
primaryKey: true,
references: {
model: 'Genres',
key: 'id'
}
})
AudioGenres.associate = function(models) {
AudioGenres.belongsTo(models.Audio, {foreignKey: 'id_audio'})
AudioGenres.belongsTo(models.Genres, {foreignKey: 'id_genre'})
};
AudioMoodModel
const AudioMoods = sequelize.define('AudioMoods', {
id_audio: {
type: DataTypes.INTEGER(11),
allowNull: false,
primaryKey: true,
references: {
model: 'Audio',
key: 'id'
}
},
id_mood: {
type: DataTypes.INTEGER(11),
allowNull: false,
primaryKey: true,
references: {
model: 'Mods',
key: 'id'
}
})
AudioMoods.associate = function(models) {
AudioMoods.belongsTo(models.Audio, {foreignKey: 'id_audio'})
AudioMoods.belongsTo(models.Mods, {foreignKey: 'id_mood'})
};
Moods and Genres Model
const Moods = sequelize.define('Moods', {
name: {
type: DataTypes.STRING(255),
allowNull: false
},
})
Moods.associate = function(models) {
Moods.belongsToMany(models.Audio, {through: 'AudioMoods', foreignKey: 'id_mood', as: 'audios'})
}
const Genres = sequelize.define('Genres', {
name: {
type: DataTypes.STRING(255),
allowNull: false
},
})
Genres.associate = function(models) {
Genres.belongsToMany(models.Audio, {through: 'AudioGenres', foreignKey: 'id_genre', as: 'audios'})
}
I suppose you should add all conditions in AND operator in both include options like this:
Audio.findAll({
include: [{
model: db.Genres,
as: "genres",
where: {
[Op.and]: [{ id: 1}, { id: 3},{ id: 2}]
},
}, {
model: db.Moods,
as: "moods",
where: {
[Op.and]: [{ id: 4}, { id: 5},{ id: 6}]
},
}]
})

sequelize many-to-many deep and nested association

For context, I am new to sequelize, and I have googled extensively to find this before reaching out to the community. I would be grateful for any help :)
I have 3 models to build a "family unit" that basically look like code below (dialect is mysql fyi). What I desire is to make a call through the user table to get a data model that looks like this:
User => { Object of User }
=> id
=> firstName
=> lastName
=> email
=> active
=> families => [ Array of Family ]
=> id
=> name
=> crew => [ Array of Users ]
=> id
=> firstName
=> lastName
=> email
=> active
This is the code I'm using to try and accomplish this and getting close but no users on the through table coming back.
User.findOne({
where: { id: 'my-guid-info'},
include: {
all: true,
nested: true,
through: {
model: "Member",
},
},
});
And here is the model code:
Family.init(
{
id: {
primaryKey: true,
type: DataTypes.UUID,
defaultValue: Sequelize.UUIDV4,
allowNull: false,
validate: {
notNull: true,
},
},
name: { type: DataTypes.STRING, required: true },
},
{
sequelize,
modelName: "Family",
timestamps: true,
paranoid: true,
});
User.init(
{
id: {
primaryKey: true,
type: DataTypes.UUID,
defaultValue: Sequelize.UUIDV4,
allowNull: false,
validate: {
notNull: true,
},
},
active: { type: DataTypes.BOOLEAN, defaultValue: true },
firstName: DataTypes.STRING,
lastName: DataTypes.STRING,
email: {
type: DataTypes.STRING,
unique: true,
required: true,
allowNull: false,
validate: {
isEmail: true,
notNull: true,
},
},
},
{
timestamps: true,
paranoid: true,
defaultScope: {
attributes: { exclude: ["hash", "salt"] },
},
});
Member.init(
{
id: {
primaryKey: true,
type: DataTypes.UUID,
defaultValue: Sequelize.UUIDV4,
allowNull: false,
validate: {
notNull: true,
},
},
userId: {
type: DataTypes.UUID,
references: { model: "User", key: "id" },
},
familyId: {
type: DataTypes.UUID,
references: { model: "Family", key: "id" },
},
relationship: {
type: DataTypes.ENUM("child", "parent", "primary"),
},
},
{
sequelize,
modelName: "Member",
timestamps: true,
});
I also have these associations setup on each model respectively.
User.associate = (models) => {
User.belongsToMany(models.Family, {
as: "families",
through: {
model: models.Member,
},
foreignKey: "userId",
otherKey: "familyId",
});
};
Family.associate = (models) => {
Family.belongsToMany(models.User, {
as: "crew",
otherKey: "userId",
attributes: {
exclude: ["hash", "salt"],
},
through: { model: models.Member },
});
};
Member.associate = (models) => {
Member.belongsTo(models.User, { foreignKey: "userId" });
Member.belongsTo(models.Family, { foreignKey: "familyId" });
};
Any direction, would be really appreciated, as I feel like I have tried all the elements of the relevant documentation, and several stackoverflow threads.

Sequelize request error SequelizeEagerLoadingError

I’m new with sequelize I’m trying to make a request with associate tables
I have a first model called Experience
module.exports = function (sequelize, DataTypes) {
const Experience = sequelize.define('experience', {
internalId: {
type: DataTypes.BIGINT,
unique: true,
allowNull: false,
},
label: {
type: DataTypes.STRING,
unique: false,
allowNull: false,
},
picture: {
type: DataTypes.TEXT,
unique: false,
allowNull: true,
},
type: {
type: DataTypes.STRING,
validate: {
isIn: {
args: [[
'generic',
'specific',
]],
msg: 'Must be a valid type',
},
},
unique: false,
allowNull: true,
},
author: {
type: DataTypes.STRING,
unique: false,
allowNull: true,
defaultValue: 'import',
},
isActive: {
type: DataTypes.BOOLEAN,
defaultValue: true,
},
});
Experience.associate = (models) => {
Experience.belongsToMany(models.Tag, {
as: 'Tags',
through: models.ExperienceTag,
});
};
return Experience;
};
a second called Tags
module.exports = function (sequelize, DataTypes) {
const Tag = sequelize.define('tag', {
internalId: {
type: DataTypes.STRING,
unique: true,
allowNull: false,
},
name: {
type: DataTypes.STRING,
unique: false,
allowNull: false,
},
author: {
type: DataTypes.STRING,
unique: false,
allowNull: true,
defaultValue: 'import',
},
isActive: {
type: DataTypes.BOOLEAN,
defaultValue: true,
},
});
Tag.associate = (models) => {
Tag.belongsToMany(models.Experience, {
as: 'Experiences',
through: models.ExperienceTag,
});
};
return Tag;
};
The association table name was ExperienceTags
I would like get all the Experiencewho have a tagId = 44
This is my request:
Experience.findAll({
include: [{
model: ExperienceTag,
where: { tagId: 44 },
}],
})
.then((results) => {
winston.warn(JSON.stringify(results, null, 2));
res.status(200)
.send(results);
})
.catch(error => res.status(500)
.send({ error: error.toString() }));
But when I execute it I have an error like:
{
"error": "SequelizeEagerLoadingError: experienceTag is not associated to experience!"
}
I think you like to include Tag rather than ExperienceTag, the following example may help you
Experience.findAll({
include: [{
model: Tag, //model name which you want to include
as: 'Tags', // you have to pass alias as you used while defining
where: { tagId: 44 },
}],
})
I think , you need to add as: 'Experiences' , in your include as you have defined association with alias
Change this
Experience.findAll({
include: [{
model: ExperienceTag,
where: { tagId: 44 },
}],
})
With
Experience.findAll({
include: [{
model: ExperienceTag,
as: 'Experiences', // <---- HERE
where: { tagId: 44 },
}],
})

Categories