Related
I am switching from using node-mssql to Sequelize in order to access my database in a simpler fashion. As a newbie to sequelize I am stumbling my way through pulling the correct data.
As I am converting a .net site with .net authentication to a node site I am using the existing authentication database. Currently I am trying to pull all roles for an existing user.
Here is the code I have so far. It returns both userID and roleID along with the username and role name that I desire. How can I remove these 2 ID columns from my query results?
test.aspnet_Users.findAll({
logging: console.log,
where: { LoweredUserName: `mcad2\\${user}` },
attributes: ['LoweredUserName'],
include: {
model: test.aspnet_Roles,
as: 'RoleId_aspnet_Roles',
attributes: ['LoweredRoleName']
}
}).then(user => {
console.log('\n\n' + JSON.stringify(user))
})
The database is set up so that both userID and roleID are contained in a third table, aspnet_UsersInRoles. This is a relatively simple 2 join query but I am not certain how to make it work using sequelize.
Here is that sequelize-auto code which was created:
const Sequelize = require('sequelize');
module.exports = function(sequelize, DataTypes) {
return sequelize.define('aspnet_Roles', {
ApplicationId: {
type: DataTypes.UUID,
allowNull: false,
references: {
model: 'aspnet_Applications',
key: 'ApplicationId'
}
},
RoleId: {
type: DataTypes.UUID,
allowNull: false,
primaryKey: true
},
RoleName: {
type: DataTypes.STRING(256),
allowNull: false
},
LoweredRoleName: {
type: DataTypes.STRING(256),
allowNull: false
},
Description: {
type: DataTypes.STRING(256),
allowNull: true
}
}, {
sequelize,
tableName: 'aspnet_Roles',
schema: 'dbo',
timestamps: false,
indexes: [
{
name: "aspnet_Roles_index1",
unique: true,
fields: [
{ name: "ApplicationId" },
{ name: "LoweredRoleName" },
]
},
{
name: "PK__aspnet_Roles__31EC6D26",
unique: true,
fields: [
{ name: "RoleId" },
]
},
]
});
};
const Sequelize = require('sequelize');
module.exports = function(sequelize, DataTypes) {
return sequelize.define('aspnet_Users', {
ApplicationId: {
type: DataTypes.UUID,
allowNull: false,
references: {
model: 'aspnet_Applications',
key: 'ApplicationId'
}
},
UserId: {
type: DataTypes.UUID,
allowNull: false,
primaryKey: true
},
UserName: {
type: DataTypes.STRING(256),
allowNull: false
},
LoweredUserName: {
type: DataTypes.STRING(256),
allowNull: false
},
MobileAlias: {
type: DataTypes.STRING(16),
allowNull: true
},
IsAnonymous: {
type: DataTypes.BOOLEAN,
allowNull: false,
defaultValue: false
},
LastActivityDate: {
type: DataTypes.DATE,
allowNull: false
}
}, {
sequelize,
tableName: 'aspnet_Users',
schema: 'dbo',
timestamps: false,
indexes: [
{
name: "aspnet_Users_Index",
unique: true,
fields: [
{ name: "ApplicationId" },
{ name: "LoweredUserName" },
]
},
{
name: "aspnet_Users_Index2",
fields: [
{ name: "ApplicationId" },
{ name: "LastActivityDate" },
]
},
{
name: "PK__aspnet_Users__03317E3D",
unique: true,
fields: [
{ name: "UserId" },
]
},
]
});
};
const Sequelize = require('sequelize');
module.exports = function(sequelize, DataTypes) {
return sequelize.define('aspnet_UsersInRoles', {
UserId: {
type: DataTypes.UUID,
allowNull: false,
primaryKey: true,
references: {
model: 'aspnet_Users',
key: 'UserId'
}
},
RoleId: {
type: DataTypes.UUID,
allowNull: false,
primaryKey: true,
references: {
model: 'aspnet_Roles',
key: 'RoleId'
}
}
}, {
sequelize,
tableName: 'aspnet_UsersInRoles',
schema: 'dbo',
timestamps: false,
indexes: [
{
name: "aspnet_UsersInRoles_index",
fields: [
{ name: "RoleId" },
]
},
{
name: "PK__aspnet_UsersInRo__35BCFE0A",
unique: true,
fields: [
{ name: "UserId" },
{ name: "RoleId" },
]
},
]
});
};
var DataTypes = require("sequelize").DataTypes;
var _aspnet_Applications = require("./aspnet_Applications");
var _aspnet_Membership = require("./aspnet_Membership");
var _aspnet_Paths = require("./aspnet_Paths");
var _aspnet_PersonalizationAllUsers = require("./aspnet_PersonalizationAllUsers");
var _aspnet_PersonalizationPerUser = require("./aspnet_PersonalizationPerUser");
var _aspnet_Profile = require("./aspnet_Profile");
var _aspnet_Roles = require("./aspnet_Roles");
var _aspnet_SchemaVersions = require("./aspnet_SchemaVersions");
var _aspnet_Users = require("./aspnet_Users");
var _aspnet_UsersInRoles = require("./aspnet_UsersInRoles");
var _aspnet_WebEvent_Events = require("./aspnet_WebEvent_Events");
var _aspnet_ZoneNumbers = require("./aspnet_ZoneNumbers");
var _aspnet_ZonePositions = require("./aspnet_ZonePositions");
function initModels(sequelize) {
var aspnet_Applications = _aspnet_Applications(sequelize, DataTypes);
var aspnet_Membership = _aspnet_Membership(sequelize, DataTypes);
var aspnet_Paths = _aspnet_Paths(sequelize, DataTypes);
var aspnet_PersonalizationAllUsers = _aspnet_PersonalizationAllUsers(sequelize, DataTypes);
var aspnet_PersonalizationPerUser = _aspnet_PersonalizationPerUser(sequelize, DataTypes);
var aspnet_Profile = _aspnet_Profile(sequelize, DataTypes);
var aspnet_Roles = _aspnet_Roles(sequelize, DataTypes);
var aspnet_SchemaVersions = _aspnet_SchemaVersions(sequelize, DataTypes);
var aspnet_Users = _aspnet_Users(sequelize, DataTypes);
var aspnet_UsersInRoles = _aspnet_UsersInRoles(sequelize, DataTypes);
var aspnet_WebEvent_Events = _aspnet_WebEvent_Events(sequelize, DataTypes);
var aspnet_ZoneNumbers = _aspnet_ZoneNumbers(sequelize, DataTypes);
var aspnet_ZonePositions = _aspnet_ZonePositions(sequelize, DataTypes);
aspnet_Roles.belongsToMany(aspnet_Users, { as: 'UserId_aspnet_Users', through: aspnet_UsersInRoles, foreignKey: "RoleId", otherKey: "UserId" });
aspnet_Users.belongsToMany(aspnet_Roles, { as: 'RoleId_aspnet_Roles', through: aspnet_UsersInRoles, foreignKey: "UserId", otherKey: "RoleId" });
aspnet_Membership.belongsTo(aspnet_Applications, { as: "Application", foreignKey: "ApplicationId"});
aspnet_Applications.hasMany(aspnet_Membership, { as: "aspnet_Memberships", foreignKey: "ApplicationId"});
aspnet_Paths.belongsTo(aspnet_Applications, { as: "Application", foreignKey: "ApplicationId"});
aspnet_Applications.hasMany(aspnet_Paths, { as: "aspnet_Paths", foreignKey: "ApplicationId"});
aspnet_Roles.belongsTo(aspnet_Applications, { as: "Application", foreignKey: "ApplicationId"});
aspnet_Applications.hasMany(aspnet_Roles, { as: "aspnet_Roles", foreignKey: "ApplicationId"});
aspnet_Users.belongsTo(aspnet_Applications, { as: "Application", foreignKey: "ApplicationId"});
aspnet_Applications.hasMany(aspnet_Users, { as: "aspnet_Users", foreignKey: "ApplicationId"});
aspnet_PersonalizationAllUsers.belongsTo(aspnet_Paths, { as: "Path", foreignKey: "PathId"});
aspnet_Paths.hasOne(aspnet_PersonalizationAllUsers, { as: "aspnet_PersonalizationAllUser", foreignKey: "PathId"});
aspnet_PersonalizationPerUser.belongsTo(aspnet_Paths, { as: "Path", foreignKey: "PathId"});
aspnet_Paths.hasMany(aspnet_PersonalizationPerUser, { as: "aspnet_PersonalizationPerUsers", foreignKey: "PathId"});
aspnet_UsersInRoles.belongsTo(aspnet_Roles, { as: "Role", foreignKey: "RoleId"});
aspnet_Roles.hasMany(aspnet_UsersInRoles, { as: "aspnet_UsersInRoles", foreignKey: "RoleId"});
aspnet_Membership.belongsTo(aspnet_Users, { as: "User", foreignKey: "UserId"});
aspnet_Users.hasOne(aspnet_Membership, { as: "aspnet_Membership", foreignKey: "UserId"});
aspnet_PersonalizationPerUser.belongsTo(aspnet_Users, { as: "User", foreignKey: "UserId"});
aspnet_Users.hasMany(aspnet_PersonalizationPerUser, { as: "aspnet_PersonalizationPerUsers", foreignKey: "UserId"});
aspnet_Profile.belongsTo(aspnet_Users, { as: "User", foreignKey: "UserId"});
aspnet_Users.hasOne(aspnet_Profile, { as: "aspnet_Profile", foreignKey: "UserId"});
aspnet_UsersInRoles.belongsTo(aspnet_Users, { as: "User", foreignKey: "UserId"});
aspnet_Users.hasMany(aspnet_UsersInRoles, { as: "aspnet_UsersInRoles", foreignKey: "UserId"});
return {
aspnet_Applications,
aspnet_Membership,
aspnet_Paths,
aspnet_PersonalizationAllUsers,
aspnet_PersonalizationPerUser,
aspnet_Profile,
aspnet_Roles,
aspnet_SchemaVersions,
aspnet_Users,
aspnet_UsersInRoles,
aspnet_WebEvent_Events,
aspnet_ZoneNumbers,
aspnet_ZonePositions,
};
}
module.exports = initModels;
module.exports.initModels = initModels;
module.exports.default = initModels;
You can add an "exclude" option in the attribute option of the query and exclude the given columns:
test.aspnet_Users.findAll({
logging: console.log,
where: { LoweredUserName: `mcad2\\${user}` },
attributes: { exclude: ['userID'], include: ['LoweredUserName'] },
include: {
model: test.aspnet_Roles,
as: 'RoleId_aspnet_Roles',
attributes: { exclude: ['roleID'], include: ['LoweredRoleName'] },
}
}).then(user => {
console.log('\n\n' + JSON.stringify(user))
})
If I understood your structure correctly this should exclude userID and roleID in the fetched data.
I have two tables Employee and Department
Department
const Department = Sequelize.define(
"Department",
{
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
},
name: {
type: DataTypes.STRING,
allowNull: false,
},
},
{
underscored: true,
timestamps: true,
paranoid: true,
modelName: "Department",
tableName: "departments",
},
);
Department.associate = function (models) {
// associations can be defined here
models.Department.hasMany(models.Employee, {
foreignKey: "department_id",
as: "employees",
});
};
return Department;
};
Employee
const Employee = Sequelize.define(
"Employee",
{
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
},
name: {
type: DataTypes.STRING,
unique: true,
allowNull: false,
},
status: {
type: DataTypes.STRING,
defaultValue: "active",
},
departmentId: {
type: DataTypes.INTEGER,
},
},
{
underscored: true,
timestamps: true,
modelName: "Employee",
tableName: "employees",
},
);
Employee.associate = function (models) {
models.Employee.belongsTo(models.Department, {
foreignKey: "department_id",
as: "department",
});
};
return Employee;
};
Now I have to fetch the list of employees and putting a filter of department_id = 1
const { departmentId } = req.body;
const employees = await Employee.findAll({
include: [
{
model: Department,
where: {
id: departmentId,
},
},
],
});
I am getting the issue. Department is mapped by association "departments"
Cannot fetch the data.
I found the answer on sequelize docs
const employees = await Employee.findAll({
include: [
{
association: "department", // this is the place to change
where: {
id: departmentId,
},
},
],
});
Learnings:
We will not be able to put association and model together.
We will be able to use the Model if no association is there.
We will be able to use association if there is one.
References: https://sequelize.org/master/manual/eager-loading.html#:~:text=You%20can%20also%20include%20by%20alias%20name%20by%20specifying%20a%20string%20that%20matches%20the%20association%20alias
I have a User Model with a hasOne relation on Role Model
User.init({
id: {
type: DataTypes.INTEGER.UNSIGNED,
autoIncrement: true,
primaryKey: true
},
name: {
type: DataTypes.STRING,
allowNull: false
},
//email, password, and other fields, ...
roleId: {
type:DataTypes.INTEGER,
allowNull: false
}},
{
sequelize,
tableName: "Users"
});
User.hasOne(Role)
and a Role Model
Role.init({
id: {
type: DataTypes.INTEGER.UNSIGNED,
autoIncrement: true,
primaryKey: true
},
name: {
type: DataTypes.STRING,
allowNull: false
}},
{
sequelize,
tableName: "Roles"
});
When I try to create a new Role with
await Role.create(req.body)
And the request is
POST http://localhost:3000/api/role
Content-Type: application/json
Authorization: Bearer <token>
{
"name": "test role"
}
I get the error column "UserId" does not exist
And the log says is:
routine: 'errorMissingColumn',
sql: 'INSERT INTO "Roles" ("id","name") VALUES (DEFAULT,$1) RETURNING "id","name","UserId";',
parameters: [
'test role'
]
What did I do wrong here? My table only has roleId in the Users table, where did the UserId in Roles table come from?
Migrations
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Roles', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
name: {
type: Sequelize.STRING,
allowNull: false
}
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Roles');
}
};
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Users', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
name: {
type: Sequelize.STRING
},
// other fields
roleId: {
type: Sequelize.INTEGER,
references: {
model: "Roles",
key: "id"
}
}
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Users');
}
};
If I add Role.belongsTo(User) in the Role Model, I get the error:
models init error: TypeError: Cannot read property 'name' of undefined
For the role to be stored in the user table as your schema suggests: User.belongsTo(Role) will setup the mapping for you as RoleId.
The Model sets up the foreign key the opposite way to the migrations, so results in a missing UserID column.
The foreign keys don't need to be defined in the schema unless you want to customise the fields. The belongsTo/hasOne options are then defined on association call.
const { Sequelize, Model, DataTypes } = require('sequelize')
const sequelize = new Sequelize('sqlite::memory:')
class User extends Model {}
class Role extends Model {}
User.init({
id: {
type: DataTypes.INTEGER.UNSIGNED,
autoIncrement: true,
primaryKey: true
},
name: {
type: DataTypes.STRING,
allowNull: false
}},
{
sequelize,
tableName: "Users"
});
Role.init({
id: {
type: DataTypes.INTEGER.UNSIGNED,
autoIncrement: true,
primaryKey: true
},
name: {
type: DataTypes.STRING,
allowNull: false
}},
{
sequelize,
tableName: "Roles"
});
User.belongsTo(Role, { foreignKey: 'roleId' })
Then you can do things with the association
async function go(){
await sequelize.sync()
const role = await Role.create({ name: 'atester' })
const user = await User.create({ name: 'test' })
await user.setRole(role)
console.log("%j", await User.findAll({ include: Role }))
}
go().catch(console.error)
Results in a document like:
{
"id": 1,
"name": "test",
"createdAt": "2020-12-04T09:44:05.762Z",
"updatedAt": "2020-12-04T09:44:05.763Z",
"roleId": 1,
"Role": {
"id": 1,
"name": "atester",
"createdAt": "2020-12-04T09:44:05.758Z",
"updatedAt": "2020-12-04T09:44:05.758Z"
}
}
From there you can match your migration to the database.
In my project, I have Users and Groups, and a User can have multiple Groups. I'm having trouble filtering a list of users based on those who are in a certain group.
Here's my setup:
User Model
module.exports = (sequelize) => {
const model = sequelize.define('users',
{
id: {
type: Sequelize.INTEGER,
autoIncrement: true,
primaryKey: true,
field: 'uID',
},
username: {
type: Sequelize.STRING, allowNull: false, unique: true, field: 'uEmail',
},
}, {
timestamps: false,
tableName: 'Users',
});
model.modelName = 'users';
return model;
};
Group Model
module.exports = (sequelize) => {
const model = sequelize.define('groups', {
id: {
type: Sequelize.INTEGER,
autoIncrement: true,
primaryKey: true,
field: 'gID',
},
name: { type: Sequelize.STRING, allowNull: false, field: 'gName' },
description: { type: Sequelize.STRING, allowNull: false, field: 'gDescription' },
}, {
timestamps: false,
tableName: 'Groups',
});
model.modelName = 'groups';
return model;
};
M2M Through Table :: UserGroups
module.exports = (sequelize) => {
const model = sequelize.define('usergroups', {
}, {
timestamps: false,
tableName: 'UserGroups',
});
model.modelName = 'usergroups';
return model;
};
Association
User.belongsToMany(Group, {
through: UserGroup,
as: 'groups',
foreignKey: 'uID',
});
Group.belongsToMany(User, {
through: UserGroup,
as: 'users',
foreignKey: 'gID',
});
My db Users look like this (in shorthand):
User1.groups = [1,2,3]
User2.groups = [1,3]
User3.groups = [2,3]
I'm building the search rest API: GET /users?groups[]=1&groups[]=2
In building the query, I've tried to filter based on the groups passed in using this format:
let query = {};
query.include = [
{
model: Group,
as: 'groups',
where: {id: {[Op.in]: filter.groups}},
attributes: ['id', 'name'],
},
];
const result = yield User.findAndCountAll(query);
or tried using through (with required: true or required: false):
let query = {};
query.include = [
{
model: Group,
as: 'groups',
through: { where: {'gID': {[Op.in]: filter.groups}}, required: true},
attributes: ['id', 'name'],
},
];
const result = yield User.findAndCountAll(query);
It kinda works, except the returned objects associated groups are abbreviated. If I pass in this filter:
GET /users?groups[]=1
The response comes back with the right users, but the groups associated with those users are filtered:
[
{
"id": 1,
"username": "user1#site.com",
"groups": [
{
"id": 1,
"name": "My Group",
"usergroups": {
"uID": 1,
"gID": 1
}
}
]
},
{
"id": 2,
"username": "user2#site.com",
"groups": [
{
"id": 1,
"name": "My Group",
"usergroups": {
"uID": 2,
"gID": 1
}
}
]
}
]
But where are groups 2 and 3 for User1, and group 2 for User2?
It's almost like I need to get all the user ids in a specific group (through the Group service class) then do:
query.where = {id: {[Op.in]: userIdsArray}}
I have tables like Medics, MedicalSpecialties and Users. Models are define like:
Users Model
const Database = require('../sequelize');
const UserModel = Database
.getInstance()
.define('users', {
UserId: {
type: Database.FIELD_TYPE_ENUM.INTEGER,
primaryKey: true,
autoIncrement: true
},
FirstName: Database.FIELD_TYPE_ENUM.STRING,
MiddleName: Database.FIELD_TYPE_ENUM.STRING,
LastName: Database.FIELD_TYPE_ENUM.STRING,
SecondLastName: Database.FIELD_TYPE_ENUM.STRING,
ID: Database.FIELD_TYPE_ENUM.STRING,
Email: Database.FIELD_TYPE_ENUM.STRING,
Password: Database.FIELD_TYPE_ENUM.STRING,
CellPhoneNumber: Database.FIELD_TYPE_ENUM.STRING,
OtherPhoneNumber: Database.FIELD_TYPE_ENUM.STRING,
Deleted: Database.FIELD_TYPE_ENUM.BOOLEAN
});
module.exports = UserModel;
Medical Specialties Model
const Database = require('../sequelize');
const MedicalSpecialtyModel = Database
.getInstance()
.define('medicalspecialties', {
MedicalSpecialtyId: {
type: Database.FIELD_TYPE_ENUM.INTEGER,
primaryKey: true,
autoIncrement: true
},
Description: Database.FIELD_TYPE_ENUM.STRING,
CreatedUserId: {
type: Database.FIELD_TYPE_ENUM.INTEGER,
references: {
model: 'users',
key: 'UserId'
}
},
UpdatedUserId: {
type: Database.FIELD_TYPE_ENUM.INTEGER,
references: {
model: 'users',
key: 'UserId'
},
allowNull: true
},
createdAt: Database.FIELD_TYPE_ENUM.DATETIME,
updatedAt: Database.FIELD_TYPE_ENUM.DATETIME
});
module.exports = MedicalSpecialtyModel;
Medics Model
const Database = require('../sequelize');
const MedicModel = Database
.getInstance()
.define('medics', {
MedicId: {
type: Database.FIELD_TYPE_ENUM.INTEGER,
primaryKey: true,
autoIncrement: true
},
UserId: {
type: Database.FIELD_TYPE_ENUM.INTEGER,
primaryKey: true,
autoIncrement: false,
references: {
model: 'users',
key: 'UserId'
}
},
MedicalSpecialtyId: {
type: Database.FIELD_TYPE_ENUM.INTEGER,
references: {
model: 'medicalspecialties',
key: 'MedicalSpecialtyId'
}
},
Code: Database.FIELD_TYPE_ENUM.STRING,
CreatedUserId: {
type: Database.FIELD_TYPE_ENUM.INTEGER,
references: {
model: 'users',
key: 'UserId'
}
},
UpdatedUserId: {
type: Database.FIELD_TYPE_ENUM.INTEGER,
references: {
model: 'users',
key: 'UserId'
},
allowNull: true
}
});
module.exports = MedicModel;
What I want to do is to get results with object parent child Representation like
[
{
"MedicId": 1,
"Code": "test1",
"user": {
"UserId": 4,
"FirstName": "John",
"MiddleName": null,
"LastName": "Doe",
"SecondLastName": null,
},
"medicalspecialty": {
"MedicalSpecialtyId": 3,
"Description": "Doctor"
}
}
]
But instead I'm getting this result:
[
{
"MedicId": 1,
"Code": "test1",
"user.UserId": 4,
"user.FirstName": "John",
"user.MiddleName": null,
"user.LastName": "Doe",
"user.SecondLastName": null,
"medicalspecialty.MedicalSpecialtyId": 3,
"medicalspecialty.Description": "Doctor"
}
]
This is how I'm pulling data:
static getAllMedics() {
MedicModel.belongsTo(MedicalSpecialtyModel, {
foreignKey: 'MedicalSpecialtyId'
});
MedicModel.belongsTo(UserModel, {
foreignKey: 'UserId'
});
UserModel.belongsTo(MedicModel);
MedicalSpecialtyModel.hasMany(MedicModel);
const attributes = ['MedicId', 'Code'];
return MedicModel.findAll({
attributes,
include: [{
model: UserModel,
attributes: ['UserId', 'FirstName', 'MiddleName', 'LastName', 'SecondLastName'],
where: {
Deleted: false
},
required: true,
nested: true
}, {
model: MedicalSpecialtyModel,
attributes: ['MedicalSpecialtyId', 'Description'],
required: true,
nested: true
}],
raw: true
});
}
Hope you can help me.
The raw property on a find call flattens the structure.
Basically do a find without raw: true.
You could get more info about it here
At the end it was my error.
When initializing Sequelize I had added the option row: true.
new Sequelize(..., {
...,
row: true
})
My apologies.