I'm working on an API in Node.js with the Sequelize ORM.
I make an API route to retrieve a random question, it works one time out of three but often returns an empty array. I can't find a solution in the documentation to prevent this...
app.get('/:certifName/:levelName/random', async function (req, res) {
return await Certification.findOne(
{
attributes: ['id', 'label'],
include: [{
model: CertificationLevel,
as: "tcl",
required: true,
attributes: ['id', 'label', 'question_number', 'exam_duration'],
include: [{
attributes: ['id', 'label'],
model: CertificationChapter,
as: "tcc",
required: true,
limit: 1,
order: [ [ Sequelize.fn('RANDOM') ] ],
include: [{
model: Question,
as: "tq",
required: true,
include: [{
model: QuestionChoice,
as: "tqc",
required: false,
attributes: ['id', 'label_fr', 'is_answer'],
}],
}],
}],
where: { label: req.params.levelName }
}],
where: { label: req.params.certifName }
})
.then(data => {
if (data) {
res.send(data);
}
else
res.sendStatus(204);
})
.catch(err => {
res.status(500).send({
message:
err.message || "Error retrieving certification details"
});
});
});
The last model :
module.exports =
class CertificationLevel extends Sequelize.Model {
static init(sequelize) {
return super.init(
{
id: {
type: Sequelize.INTEGER,
autoIncrement: true,
primaryKey: true
},
label: Sequelize.STRING,
slug: Sequelize.STRING,
description: Sequelize.TEXT,
question_number: Sequelize.INTEGER,
exam_duration: Sequelize.INTEGER,
created_at: {
type: Sequelize.DATE,
defaultValue: Sequelize.NOW
},
updated_at: Sequelize.DATE,
deleted_at: Sequelize.DATE
},
{
tableName: 't_certifications_levels',
sequelize,
underscored: true,
timestamps: false
},
);
}
static associate(models) {
this.belongsTo(models.Certification, {
onDelete: "CASCADE",
foreignKey: {
allowNull: false,
name: "certification_id"
}
}),
this.hasMany(models.CertificationChapter, { as:'tcc', foreignKey: 'level_id' })
this.hasMany(models.Subscription, { as:'ts',foreignKey: 'level_id' })
}
}
And this is a SQL query generate :
SELECT "Certification".*,
"tcl"."id" AS "tcl.id",
"tcl"."label" AS "tcl.label",
"tcl"."question_number" AS "tcl.question_number",
"tcl"."exam_duration" AS "tcl.exam_duration"
FROM
(SELECT "Certification"."id",
"Certification"."label"
FROM "t_certifications" AS "Certification"
WHERE "Certification"."label" = 'ISTQB'
AND
(SELECT "tcl"."certification_id"
FROM "t_certifications_levels" AS "tcl"
WHERE ("tcl"."label" = 'Fondation'
AND "tcl"."certification_id" = "Certification"."id")
LIMIT 1) IS NOT NULL
LIMIT 1) AS "Certification"
INNER JOIN "t_certifications_levels" AS "tcl" ON "Certification"."id" = "tcl"."certification_id"
AND "tcl"."label" = 'Fondation';
I don't know why I have two executing to my route?
I am a real beginner in back development...
Thank for you help
Related
I have a problem in findById data with relational sequelize.
Where I have the condition of the One-to-One and Many-to-Many relations
TB_User is related on-to-one with tb_corporate
TB_User is related to many-to-many with TB Corporate and this data relations are saved in TB_corporate_Group
My problem, when I want to get corporate and corporate group. The data I get is data from the user table where the relationship is one-to-one.
this is code in model users
module.exports = (sequelize :any ) => {
class users extends Model{
static associate(models : any) {
// define association here
users.belongsToMany(models.corporate, {
through: 'corporateGroup',
as: 'dataCorporateGroup'
})
users.hasOne(models.corporate, {foreignKey: 'userId', as: 'corporate'})
}
}
users.init({
id: {
type: DataTypes.INTEGER,
allowNull: false,
primaryKey: true,
autoIncrement: true
},
name: {
field: 'name',
type: DataTypes.STRING,
allowNull: false,
},
}, {
sequelize,
modelName: 'users',
});
return users;
};
code for model corporate
module.exports = (sequelize :any ) => {
class corporate extends Model{
static associate(models : any) {
// define association here
corporate.belongsTo(models.users, {foreignKey: 'userId', as: 'userCorporate'})
corporate.belongsToMany(models.users, {
through: 'corporateGroup',
as: 'userCorporate'
})
}
}
corporate.init({
userId: {
field: 'user_id',
type: DataTypes.INTEGER,
allowNull: false,
unique: true,
},
corporateName: {
field: 'name',
type: DataTypes.STRING,
allowNull: false,
unique: true,
},
}, {
sequelize,
modelName: 'corporate',
});
return corporate;
};
code for model corporate group
module.exports = (sequelize :any ) => {
class corporateGroup extends Model{
static associate(models : any) {
// define association here
}
}
corporateGroup.init({
corporateId: {
field: 'corporate_id',
type: DataTypes.INTEGER,
allowNull: false,
references: {
model: 'corporate',
key: 'id'
}
},
userId: {
field: 'user_id',
type: DataTypes.INTEGER,
allowNull: false,
references: {
model: 'users',
key: 'id'
}
}
}, {
sequelize,
tableName: 'corporate_group',
modelName: 'corporateGroup',
});
return corporateGroup;
};
and this code for find by id
await corporate.findById(id, {
include: [{
model: db.users,
as: 'userCorporate',
}]
})
You need to indicate the unique aliases for associations
corporate.belongsTo(models.users, {foreignKey: 'userId', as: 'userCorporate'})
corporate.belongsToMany(models.users, {
through: 'corporateGroup',
as: 'corporateGroupUsers'
})
and then you can indicate included models with these aliases to get both of them along with corporate record:
await corporate.findById(id, {
include: [{
model: db.users,
as: 'userCorporate',
}, {
model: db.users,
as: 'corporateGroupUsers',
}]
})
I'm fairly new to using Sequelize and I'm having issues with findAndCountAll while using filters with eager loading. Following are the two models and their definitions.
const Campaign = sequelize.define(
"campaign",
{
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.BIGINT,
},
name: {
type: Sequelize.STRING(512),
},
advertiser_id: {
type: Sequelize.BIGINT,
},
},
{
timestamps: true,
createdAt: "created_at",
updatedAt: "modified_at",
});
Campaign.associate = function (model) {
Campaign.hasMany(model.performanceData, {
as: "performanceData",
foreignKey: "campaign_id",
constraints: false,
});
};
const PerformanceData = sequelize.define(
"performanceData",
{
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.BIGINT,
},
campaign_id: {
type: Sequelize.BIGINT,
},
clicks: {
type: Sequelize.BIGINT,
},
date: {
type: Sequelize.STRING(20),
},
},
{
tableName: "performance_data",
timestamps: true,
createdAt: "created_at",
updatedAt: "modified_at",
});
One campaign will have multiple performance data records based on date. I want to get all the campaigns by an advertiser (paginated) while eager loading the aggregate of performance data. So the performance data returned should be aggregate of a particular campaign.
Following is the query I'm using but it doesn't return the data:
const campaigns = await Campaign.findAndCountAll({
where: {
advertiser_id: reqObject.advertiser_id,
},
include: {
as: "performanceData",
model: PerformanceData,
attributes: [],
},
attributes: {
include: [
[
db.Sequelize.fn("SUM", db.Sequelize.col("performanceData.clicks")),
"clicks",
],
],
},
group: ["campaign.id"],
offset: reqObject.page,
limit: reqObject.size});
Can anyone please help with this?
I'm not sure if I am missing something, but the Sequelize seems not to create the methods derived from belongsToMany(...) Association (in this case: setRequest, getRequest, addRequest Sequelize Docs)
I'm trying to create a N:N relationship (With Foods and Requests). So, a request can have many foods, and a food can have many requests
The error I get:
(node:4504) UnhandledPromiseRejectionWarning: TypeError: food.addRequests is not a function
Models
Request Model:
const { Model, DataTypes } = require('sequelize');
class Request extends Model {
static init(sequelize) {
super.init({
//Format
client: DataTypes.STRING(70),
time_processing: DataTypes.INTEGER,
time_cooking: DataTypes.INTEGER,
time_paying: DataTypes.INTEGER,
//Has one Table
}, {
//Conection
sequelize
})
}
static associate(models) {
this.belongsTo(models.Table, { foreignKey: 'fk_table', as: 'table', targetKey: 'id' });
this.belongsTo(models.Employee, { foreignKey: 'fk_employee', as: '' });
this.belongsToMany(models.Food, { foreignKey: 'fk_request', through: 'requests_foods', as: 'request' });
}
}
module.exports = Request;
Food Model:
const { Model, DataTypes } = require('sequelize');
class Food extends Model {
static init(sequelize) {
super.init({
//Format
name: DataTypes.STRING(40),
image: DataTypes.STRING,
price: DataTypes.FLOAT,
storage: DataTypes.INTEGER,
//Is used By Many Requests
}, {
//Conection
sequelize,
tableName: 'foods'
})
}
static associate(models) {
//Through: Table used to join data
//foreignKey: Which column of requests_foods is used to reference to models.Request
this.belongsToMany(models.Request, { foreignKey: 'fk_food', through: 'requests_foods', as: 'foods' });
this.belongsTo(models.Category, { foreignKey: 'fk_category', as: 'category' });
}
}
module.exports = Food;
The pivot table:
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('requests_foods', {
id: {
type: Sequelize.INTEGER,
autoIncrement: true,
primaryKey: true,
allowNull: false,
},
fk_request: {
type: Sequelize.INTEGER,
allowNull: false,
references: { model: 'requests', key: 'id' },
onUpdate: 'CASCADE',
onDelete: 'CASCADE',
},
fk_food: {
type: Sequelize.INTEGER,
allowNull: false,
references: { model: 'foods', key: 'id' },
onUpdate: 'CASCADE',
onDelete: 'RESTRICT'
},
amount: {
type: Sequelize.INTEGER,
allowNull: false,
},
created_at: {
type: Sequelize.DATE,
allowNull: false,
},
updated_at: {
type: Sequelize.DATE,
allowNull: false,
}
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('requests_foods');
}
};
And Finally, the Controller (Where the error is thrown)
const Request = require('../models/Request');
const Food = require('../models/Food');
module.exports = {
async list(req, res) {
const dbResponse = await Request.findAll();
return res.json(dbResponse);
},
async add(req, res) {
const { client, time_processing, time_cooking, time_paying, fk_table, fk_employee, foods } = req.body;
console.log(req.body);
const dbResponse = await Request.create({ client, time_processing, time_cooking, time_paying, fk_table, fk_employee });
//Look for food list
foods.map(async item => {
console.log('ITEM:', item);
const food = await Food.findByPk(item.fk_food)
console.log(food);
food.addRequests(); //ERROR HERE
})
return res.json(dbResponse);
}
}
Notes:
The request is added, but the requests_foods not.
I tried changing to: addRequest(), setRequests, setRequest(), but nothing works, I console.log the object, and it seems that the object just doesn't have any method:
{ client: 'Wesley Almeida Braga',
time_processing: 0,
time_cooking: 0,
time_paying: 0,
fk_table: 1,
fk_employee: 1,
foods: [ { fk_food: 1 } ] }
Executing (default): INSERT INTO `requests` (`id`,`client`,`time_processing`,`time_cooking`,`time_paying`,`created_at`,`updated_at`,`fk_employee`,`fk_table`) VALUES (DEFAULT,?,?,?,?,?,?,?,?);
ITEM: { fk_food: 1 }
Executing (default): SELECT `id`, `name`, `image`, `price`, `storage`, `created_at` AS `createdAt`, `updated_at` AS `updatedAt`, `fk_category` FROM `foods` AS `Food` WHERE `Food`.`id` = 1;
Food {
dataValues:
{ id: 1,
name: 'Carne de Sol',
image: 'example_2.png',
price: 25,
storage: 10,
createdAt: 2020-01-12T03:48:34.000Z,
updatedAt: 2020-01-12T03:48:34.000Z,
fk_category: 1 },
_previousDataValues:
{ id: 1,
name: 'Carne de Sol',
image: 'example_2.png',
price: 25,
storage: 10,
createdAt: 2020-01-12T03:48:34.000Z,
updatedAt: 2020-01-12T03:48:34.000Z,
fk_category: 1 },
_changed: {},
_modelOptions:
{ timestamps: true,
validate: {},
freezeTableName: false,
underscored: true,
paranoid: false,
rejectOnEmpty: false,
whereCollection: { id: 1 },
schema: null,
schemaDelimiter: '',
defaultScope: {},
scopes: {},
indexes: [],
name: { plural: 'Food', singular: 'Food' },
omitNull: false,
sequelize:
Sequelize {
options: [Object],
config: [Object],
dialect: [MysqlDialect],
queryInterface: [QueryInterface],
models: [Object],
modelManager: [ModelManager],
connectionManager: [ConnectionManager],
importCache: {} },
tableName: 'foods',
hooks: {} },
_options:
{ isNewRecord: false,
_schema: null,
_schemaDelimiter: '',
raw: true,
attributes:
[ 'id',
'name',
'image',
'price',
'storage',
'createdAt',
'updatedAt',
'fk_category' ] },
isNewRecord: false }
Hello guys I am new to sequalize and I am facing difficulty to join multiple tables.
Sequalize throws error below:
SequelizeEagerLoadingError: customers is associated to estimations using an alias. You've included an alias (createdby_details), but it does not match the alias(es) defined in your association (customer_details).
Here is my Estimation model:
var Customer = require('../models/Customer')
var User = require('../models/User')
var Estimations = connection.sequelize.define('estimations',{
id:{
type: connection.Sequelize.INTEGER,
autoIncrement: true,
primaryKey: true
},
user_id: {
type: connection.Sequelize.INTEGER
},
title: {
type: connection.Sequelize.TEXT
},
customer_id: {
type: connection.Sequelize.INTEGER
},
project_address: {
type: connection.Sequelize.TEXT
},
bid: {
type: connection.Sequelize.TEXT
},
due_date: {
type: connection.Sequelize.DATEONLY
},
total_cost: {
type: connection.Sequelize.INTEGER
},
created_by: {
type: connection.Sequelize.INTEGER
},
is_deleted: {
type: connection.Sequelize.TINYINT
},
created_at: {
type: connection.Sequelize.DATE
},
updated_at: {
type: connection.Sequelize.DATE
}
},{
timestamps: true,
createdAt: 'created_at',
updatedAt: 'updated_at'
});
Estimations.belongsTo(Customer, {
as: 'customer_details',
foreignKey: 'customer_id',
targetKey: 'id'
});
Estimations.belongsTo(User, {
as: 'createdby_details',
foreignKey: 'created_by',
targetKey: 'id'
});
Here is my controller code:
EstimationSchema.findAndCountAll({
include: [
{
attributes: ['org_name'],
model: CustomerSchema,
as: "customer_details"
},
{
attributes: ['first_name'],
model: UserSchema,
as: "createdby_details"
}
],
attributes: columnName,
where: whereobj,
limit: parseInt(req.body.length),
offset: parseInt(req.body.start),
order: [ sort ]
}).then(records => {
response.recordsTotal = records.count;
response.recordsFiltered = records.count
response.data = records.rows
}).catch(err => {
})
I have one another model 'User' which has primary key 'id' and it has to match with my above 'Estimation' model with 'created_by' field and fetch that user data.
What I want to do is to fetch 'Estimation' table data with associated 'Customer' and 'User' table data.
Thanks in advance.
When I try to create a new Conversation item Sequelize will return an object with id: null eventhough there is an valid id in the database. How can I get Sequelize to return the last inserted id to the newly created item?
Conversation.create({
type: 'private',
createdBy: 1,
}).then(conversation => {
reply(conversation);
});
Will return
{
"type": "conversations",
"id": null,
"createdBy": 1,
"created_at": "2016-03-18T01:47:48.000Z"
}
My code:
const Conversation = model.define('Conversation', {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
},
type: {
type: Sequelize.ENUM,
values: ['private', 'group'],
validate: {
isIn: ['private', 'group'],
},
},
createdBy: {
type: Sequelize.INTEGER,
field: 'created_by',
},
}, {
tableName: 'conversations',
timestamps: true,
createdAt: 'created_at',
updatedAt: false,
getterMethods: {
type: () => 'conversations',
},
});
const User = model.define('User', {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
},
firstName: {
type: Sequelize.STRING,
field: 'first_name',
allowNull: false,
},
lastName: {
type: Sequelize.STRING,
field: 'last_name',
allowNull: true,
},
email: {
type: Sequelize.STRING,
allowNull: false,
},
profileImg: {
type: Sequelize.STRING,
field: 'profile_img',
allowNull: false,
},
password: Sequelize.STRING,
}, {
tableName: 'users',
timestamps: true,
createdAt: 'created_at',
updatedAt: 'updated_at',
getterMethods: {
type: () => 'users',
},
});
Conversation.belongsToMany(User, {
foreignKey: 'conversation_id',
otherKey: 'user_id',
through: 'conversation_user',
timestamps: false,
});
User.belongsToMany(Conversation, {
as: 'conversations',
foreignKey: 'user_id',
otherKey: 'conversation_id',
through: 'conversation_user',
timestamps: false,
});
Yo need to put autoIncrement: true in id field:
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true,
}
Personally I would advice to skip the id column as sequalize does it automatically for you and works nicely.
hope it helps :)
Problem was my MySQL version (5.6 instead of 5.7), updated it and now I'm getting id's of the created items in the promise.
I'm not sure about how Sequelize is working with id field, I get null if I do instance.id, bug I can get the real value at DB if I do the following:
console.info(instance.id); // null
console.info(instance.get('id')); // 25 => Real ID
console.info(instance.getDataValue('id')); // 25 => Real ID
Something similar is happening with other fields like createdAt and updatedAt.
In order to get the real value at id field and other related fields, I added following logic to Model declaration:
class FooModel extends Model {
// ...
/**
* #inheritdoc
*/
public async save(options?: SaveOptions<TModelAttributes>): Promise<this> {
await super.save(options);
this.loadBaseData();
return this;
}
/**
* #inheritdoc
*/
public async reload(options?: FindOptions<TModelAttributes>): Promise<this> {
await super.reload(options);
this.loadBaseData();
return this;
}
private loadBaseData() {
this.id = this.getDataValue('id');
this.createdAt = this.getDataValue('createdAt');
this.updatedAt = this.getDataValue('updatedAt');
}
}
because if you only build without save it then:
instance.id // null
so you need:
instance.save()
instance.id // someNumber