Sequelize says instanceMethod is not defined - mysql

I'm using sequelize to connect to a mysql db for development. I have a model called Dealer:
'use strict';
module.exports = function(sequelize, DataTypes) {
var Dealer = sequelize.define('Dealer', {
id: { allowNull: false, autoIncrement: true,
primaryKey: true, type: DataTypes.INTEGER.UNSIGNED },
...
created_at: { allowNull: false, type: DataTypes.DATE },
updated_at: { allowNull: false, type: DataTypes.DATE }
},
{underscored: true},
{
classMethods: {
associate: function(models) {
Dealer.hasMany(models.Job);
}
},
instanceMethods: {
getAllClientData: function(){
leads = [];
...
return leads;
},
}
});
return Dealer;
};
When I try to call the instance method on an object returned by a sequelize query in my dealerController.js file:
dealer.getAllClientData()
I get the error:
Unhandled rejection TypeError: dealer.getAllClientData is not a function
When i print the returned JSON to the console it reads as such:
{ dataValues:
{ id: 1,
....
}
...
'$modelOptions':
{ timestamps: true,
instanceMethods: {},
classMethods: {},
validate: {},
freezeTableName: false,
underscored: true,
underscoredAll: false,
paranoid: false,
rejectOnEmpty: false,
whereCollection: { id: '1' },
schema: null,
schemaDelimiter: '',
defaultScope: {},
scopes: [],
hooks: {},
indexes: [],
name: { plural: 'Dealers', singular: 'Dealer' },
omitNul: false,
...
}
...
}
Obviously my instanceMethod is not defined, and according to the sequelize docs I should have getters and setters available too.
I don't understand what step i'm missing here as I've read through much of the sequelize docs and even used their cli to generate the models and migrations.
Any thoughts?
Edit:
Here is what is output to log for dealer.prototype
{ _customGetters: {},
_customSetters: {},
validators: {},
_hasCustomGetters: 0,
_hasCustomSetters: 0,
rawAttributes:
{ id:
{ allowNull: false,
autoIncrement: true,
primaryKey: true,
type: [Object],
Model: Dealer,
fieldName: 'id',
_modelAttribute: true,
field: 'id' },
///Other Attributes
},
_isAttribute: { [Function] cache: MapCache { __data__: [Object] } },
Model: Dealer,
$Model': Dealer }

After reading the docs a little further and looking at some other model definitions I discovered that the issue was I had defined my model incorrectly.
In my definition above you'll notice I wrapped the underscored: true option in brackets, followed by my classMethods and instanceMethods wrapped in another set of brackets.
This is incorrect. The proper way to define a sequelize model is with two sets of brackets, the first containing your model attributes and the second containing all other options, including methods.
'use strict';
module.exports = function(sequelize, DataTypes) {
var Dealer = sequelize.define('Dealer', {
id: { allowNull: false, autoIncrement: true,
primaryKey: true, type: DataTypes.INTEGER.UNSIGNED },
...
created_at: { allowNull: false, type: DataTypes.DATE },
updated_at: { allowNull: false, type: DataTypes.DATE }
},
{
underscored: true,
classMethods: {
associate: function(models) {
Dealer.hasMany(models.Job);
}
},
instanceMethods: {
getAllClientData: function(){
leads = [];
...
return leads;
},
}
});
return Dealer;
};

Related

How to define an array in my models using mysql sequelize | node.js

This is the result what I need to store in DB :
sellers : [{'test1'},{'test2'},{'test3'}]
And this is my model:
module.exports = (sequelize, DataTypes) => {
const User = sequelize.define(
"User",
{
id: {
allowNull: false,
primaryKey: true,
autoIncrement: true,
type: DataTypes.INTEGER,
},
name: {
type: DataTypes.STRING,
allowNull: false,
},
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
},
password: {
type: DataTypes.STRING,
allowNull: false,
},
sellers: [
{
type: DataTypes.STRING,
allowNull: true,
},
],
},
{
timestamps: true,
defaultScope: {
attributes: {
exclude: ["password"],
},
},
scopes: {
withPassword: {
attributes: {},
},
},
indexes: [
{
unique: true,
fields: ["email"],
},
],
}
);
return User;
};
And the error is :
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near
sellers: [
{
type: DataTypes.ARRAY(DataTypes.STRING),
allowNull: true,
}
You need to slightly change your schema definition as,
sellers: {
type: DataTypes.ARRAY(DataTypes.JSON),
allowNull: true,
defaultValue: [{}],
get() {
const data = this.getDataValue('sellers');
const queryResponse = [];
data.forEach(seller => {
queryResponse.push(JSON.parse(seller));
});
return queryResponse;
},
set(seller) {
return this.setDataValue('sellers', JSON.stringify(seller));
}
},

Sequelize belongsTo query results in belongsTo called with something that's not a subclass of Sequelize.Model error

I am trying to write a join query using belongsTo but getting error:
Error: LearningCertificateEvent.belongsTo called with something that's
not a subclass of Sequelize.Model
at Function. (/Users/msmexmac/node/msmex-backend/msmex-api/node_modules/sequelize/lib/associations/mixin.js:93:13)
I am trying to fetch events related to learning cerfificate events. I tried every thing but it does not work somehow.
My models are as follows:
LearningCertificate Model
"use strict";
const Sequelize = require("sequelize");
const sequelize = require("../helpers/db");
const LearningCertificateEvent = require("./LearningCertificateEvent");
const LearningCertificate = sequelize.define(
"LearningCertificate",
{
id: {
autoIncrement: true,
type: Sequelize.BIGINT,
primaryKey: true,
},
userId: {
type: Sequelize.INTEGER,
field: "user_id",
},
certificateURL: {
type: Sequelize.STRING,
field: "certificate_url",
},
certificatePNGURL: {
type: Sequelize.STRING,
field: "certificate_png_url",
},
certificateType: {
type: Sequelize.STRING,
field: "certificate_type",
},
createdAt: {
type: Sequelize.BIGINT,
field: "created_at",
},
updatedAt: {
type: Sequelize.BIGINT,
field: "updated_at",
},
},
{
timestamps: false,
tableName: "learning_certificate",
}
);
LearningCertificate.hasMany(LearningCertificateEvent, {
foreignKey: "certificateId",
});
module.exports = LearningCertificate;
The second model is : LearningCertificateEvent Model
"use strict";
const Sequelize = require("sequelize");
const sequelize = require("../helpers/db");
const MsmexEvent = require("./MsmexEvent");
const LearningCertificateEvent = sequelize.define(
"LearningCertificateEvent",
{
id: {
autoIncrement: true,
type: Sequelize.BIGINT,
primaryKey: true,
},
userId: {
type: Sequelize.INTEGER,
field: "user_id",
},
certificateId: {
type: Sequelize.INTEGER,
field: "certificate_id",
},
eventId: {
type: Sequelize.INTEGER,
field: "event_id",
},
createdAt: {
type: Sequelize.BIGINT,
field: "created_at",
},
updatedAt: {
type: Sequelize.BIGINT,
field: "updated_at",
},
},
{
timestamps: false,
tableName: "learning_certificate_event",
}
);
LearningCertificateEvent.belongsTo(MsmexEvent, {
foreignKey: "eventId",
});
// MsmexEvent.hasMany(LearningCertificateEvent, {
// foreignKey: "eventId",
// });
module.exports = LearningCertificateEvent;
And last model is: MsmexEvent Model
"use strict";
const Sequelize = require("sequelize");
const sequelize = require("../helpers/db");
const LearningCertificateEvent = require("./LearningCertificateEvent");
const Event = sequelize.define(
"Event",
{
id: {
autoIncrement: true,
type: Sequelize.BIGINT,
primaryKey: true,
},
identifier: {
type: Sequelize.STRING,
field: "identifier",
},
expertId: {
type: Sequelize.INTEGER,
field: "expert_id",
},
waitList: {
type: Sequelize.BOOLEAN,
field: "waitList",
},
},
{
timestamps: false,
tableName: "msmex_event",
}
);
module.exports = Event;
The query which I have written is as follows:
return LearningCertificate.findAll({
where: {
userId: userId,
},
include: [
{
model: LearningCertificateEvent,
required: true,
include: [
{
model: MsmexEvent,
required: true,
},
],
},
],
order: [["id", "DESC"]],
});
In query however if I replace MsmexEvent with some other table for example User it works fine.
Can some one please help ?

Sequelize returning one result when counting AVG in Eager Loading

I have been trying to fix this problem for a day now but no luck. I am using Sequelize With Nodejs and MySQL dialect.
I am querying for Influencers whilst also calculating their average ratings from a InfluencerRating record. They are associated through a oneToMany relation.
Here is my Influencer modal:
const Sequelize = require('sequelize');
module.exports = function(sequelize, DataTypes) {
return sequelize.define('influencer', {
id: {
autoIncrement: true,
type: DataTypes.INTEGER,
allowNull: false,
primaryKey: true
},
display_name: {
type: DataTypes.STRING(255),
allowNull: false
},
link_facebook: {
type: DataTypes.STRING(255),
allowNull: true
},
link_soundcloud: {
type: DataTypes.STRING(255),
allowNull: true
},
link_twitter: {
type: DataTypes.STRING(255),
allowNull: true
},
link_bandcamp: {
type: DataTypes.STRING(255),
allowNull: true
},
link_spotify: {
type: DataTypes.STRING(255),
allowNull: true
},
link_instagram: {
type: DataTypes.STRING(255),
allowNull: true
},
link_deezer: {
type: DataTypes.STRING(255),
allowNull: true
},
link_youtube: {
type: DataTypes.STRING(255),
allowNull: true
},
link_website: {
type: DataTypes.STRING(255),
allowNull: true
},
description_english: {
type: DataTypes.STRING(255),
allowNull: true
},
description_french: {
type: DataTypes.STRING(255),
allowNull: true
},
information_english: {
type: DataTypes.STRING(255),
allowNull: true
},
information_french: {
type: DataTypes.STRING(255),
allowNull: true
},
enabled: {
type: DataTypes.BOOLEAN,
allowNull: false,
defaultValue: 0
},
profile_image: {
type: DataTypes.STRING(255),
allowNull: true
},
banner_image: {
type: DataTypes.STRING(255),
allowNull: true
},
admin_public_status: {
type: DataTypes.BOOLEAN,
allowNull: false,
defaultValue: 1
}
}, {
sequelize,
tableName: 'influencer',
timestamps: false,
underscored: true,
indexes: [
{
name: "PRIMARY",
unique: true,
using: "BTREE",
fields: [
{ name: "id" },
]
},
]
});
};
Here is my InfluencerRating modal:
module.exports = function(sequelize, DataTypes) {
return sequelize.define('influencer_rating', {
id: {
autoIncrement: true,
type: DataTypes.INTEGER,
allowNull: false,
primaryKey: true
},
description: {
type: DataTypes.STRING(255),
allowNull: false
},
influencer_id: {
type: DataTypes.INTEGER,
allowNull: false,
references: {
model: 'influencer',
key: 'id'
}
},
rating: {
type: DataTypes.INTEGER,
allowNull: false
}
}, {
sequelize,
tableName: 'influencer_rating',
timestamps: false,
underscored: true,
indexes: [
{
name: "PRIMARY",
unique: true,
using: "BTREE",
fields: [
{ name: "id" },
]
},
{
name: "type_id",
using: "BTREE",
fields: [
{ name: "influencer_id" },
]
},
]
});
};
And here is my associations between them both:
influencer_rating.belongsTo(influencer, { as: "influencer", foreignKey: "influencer_id"});
influencer.hasMany(influencer_rating, { as: "influencer_ratings", foreignKey: "influencer_id"});
Here is my query to find influencers and calculate their average ratings:
var influencers = await Influencer.findAll(
{
where: {
[db.Op.and]: [
{
enabled: true
},
{
display_name: {
[db.Op.like]: '%' + filter + '%'
}
},
{
admin_public_status: true
}
],
},
include: [
[
{
model: InfluencerRating,
as: "influencer_ratings",
required: false,
attributes: [[Sequelize.fn('AVG', Sequelize.col('rating')), 'rating']]
}
],
limit: 10,
offset: 0,
group: ['id']
}
);
The query should be returning 2 influencer Objects, but I am only getting one.
I get the wanted behaviour if I delete my :
attributes: [[Sequelize.fn('AVG', Sequelize.col('rating')), 'rating']]
I have tried calculating my average outside of my InfluencerRating include but not luck.
Made some changes to findAll. Can give it a try:
var influencers = await Influencer.findAll({
// whenever hasMany used in include, the main query gets converted to subquery
// if limit + offset is applied
subQuery: false, // <----- instructs not to make a sub query
attributes: {
include: [
[Sequelize.fn('AVG', Sequelize.col('influencer_ratings.rating')), 'total_rating']
],
},
where: {
[db.Op.and]: [
{ enabled: true },
{ display_name: { [db.Op.like]: '%' + filter + '%' } },
{ admin_public_status: true }
],
},
include: [ // removed extra nesting of array
{
model: InfluencerRating,
as: "influencer_ratings",
required: false,
attributes: [],
}
],
limit: 10,
offset: 0,
group: [
'id',
// 'influencer_ratings.influencer_id', // add if sql error occurs
],
order: [ // if needed
[Sequelize.literal('total_rating'), 'DESC'],
]
});
Few reference links:
Disable sub query generation(done by sequelize) when hasMany/belongsToMany relation included - https://github.com/sequelize/sequelize/issues/1756#issuecomment-54748245
Using SQL sub queries with ordering: https://sequelize.org/master/manual/sub-queries.html
Alternate approach:
var influencers = await Influencer.findAll({
attributes: {
include: [
[Sequelize.literal(`(
SELECT
AVG("influencer_ratings"."rating") AS "avg_rating"
FROM
"influencer" AS "influencer1"
LEFT OUTER JOIN (
"influencer_ratings" AS "influencer_ratings"
) ON "influencer1"."id" = "influencer_ratings"."influencer_id"
WHERE
"influencer1"."id" = "influencer"."id"
GROUP BY
"influencer1"."id"
)`), 'avg_rating'], // now this is just a subquery and will not interfere with other includes
],
},
where: {
[db.Op.and]: [
{ enabled: true },
{ display_name: { [db.Op.like]: '%' + filter + '%' } },
{ admin_public_status: true }
],
},
include: [ // removed extra nesting of array
// influencer_ratings include not needed now
// add other includes as needed
],
limit: 10,
offset: 0,
order: [ // if needed
[Sequelize.literal('avg_rating'), 'DESC'],
]
});

Can i use `SET` variable in sequelize in nodejs

Can i use SET variable in sequelize in nodejs?
Sequelize
Model
Session Model
"use strict";
module.exports = (sequelize, DataTypes) => {
const Sessions = sequelize.define(
"sessions",
{
id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
challenge_id: {
type: DataTypes.INTEGER,
references: {
model: "challenges",
key: "id"
},
allowNull: false
},
createdAt: { type: DataTypes.DATE },
createdBy: {
type: DataTypes.INTEGER,
references: {
model: "users",
key: "id"
},
allowNull: false
},
showAtLeaderboard: { type: DataTypes.ENUM("yes", "no") },
sessionFile: { type: DataTypes.STRING },
score: { type: DataTypes.INTEGER },
},
{
timestamps: false,
underscored: true
}
);
return Sessions;
};
Challenge Model
"use strict";
module.exports = (sequelize, DataTypes) => {
const Challenges = sequelize.define(
"challenges",
{
id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
song_id: { type: DataTypes.INTEGER },
challenge_name: { type: DataTypes.STRING },
challengeDescription: { type: DataTypes.STRING },
challengeImg: { type: DataTypes.STRING },
challengeType: { type: DataTypes.STRING },
coins: { type: DataTypes.INTEGER },
createdAt: { type: DataTypes.DATE },
expireAt: { type: DataTypes.DATE },
isActive: { type: DataTypes.ENUM("yes", "no") },
tags: { type: DataTypes.STRING }
},
{
timestamps: false,
underscored: true
}
);
return Challenges;
};
Relationship
db.challenges.hasMany(db.sessions, {
foreignKey: "challenge_id",
sourceKey: "id"
});
Can I perform below SQL query with sequelize Orm model?
SET #rank=0
SELECT `challenges`.`*`, `sessions`.*, #rank:=#rank+1 AS rank FROM `challenges` JOIN `sessions` ON `sessions`.`challenge_id` = `challenges`.`id` ORDER BY `sessions`.`score` DESC
Sequelize support this type of query with it's ORM Model.

Sequelize model prototype show add function, but fails when executed

I am having trouble adding users to my boards model using sequelize. My associations are defined as follows:
module.exports = function(sequelize, DataTypes) {
var Board = sequelize.define("Board", {
name: {
type: DataTypes.STRING,
allowNull: false,
validate: {
len: [1]
}
},
favorited: {
type: DataTypes.BOOLEAN,
allowNull: false,
defaultValue: false
}
});
Board.associate = function(models) {
Board.belongsTo(models.User, {
foreignKey: {
name: "OwnerId"
}
});
Board.belongsToMany(models.User, {
through: "UserBoards"
});
};
return Board;
};
And my user model:
module.exports = function(sequelize, DataTypes) {
var User = sequelize.define("User", {
name: {
type: DataTypes.STRING,
allowNull: false,
validate: {
isAlphanumeric: true
}
},
email: {
type: DataTypes.STRING,
allowNull: false,
validate: {
isEmail: true
}
},
password: {
type: DataTypes.STRING,
allowNull: false
}
});
User.associate = function(models) {
User.belongsToMany(models.Board, {
through: "UserBoards"
});
};
return User;
};
Console logging Boards.prototype gives me the following:
Board {
_customGetters: {},
_customSetters: {},
validators: { name: { len: [Object] } },
_hasCustomGetters: 0,
_hasCustomSetters: 0,
rawAttributes:
{ id:
{ type: [Object],
allowNull: false,
primaryKey: true,
autoIncrement: true,
_autoGenerated: true,
Model: Board,
fieldName: 'id',
_modelAttribute: true,
field: 'id' },
name:
{ type: [Object],
allowNull: false,
validate: [Object],
Model: Board,
fieldName: 'name',
_modelAttribute: true,
field: 'name' },
favorited:
{ type: BOOLEAN {},
allowNull: false,
defaultValue: false,
Model: Board,
fieldName: 'favorited',
_modelAttribute: true,
field: 'favorited' },
createdAt:
{ type: [Object],
allowNull: false,
_autoGenerated: true,
Model: Board,
fieldName: 'createdAt',
_modelAttribute: true,
field: 'createdAt' },
updatedAt:
{ type: [Object],
allowNull: false,
_autoGenerated: true,
Model: Board,
fieldName: 'updatedAt',
_modelAttribute: true,
field: 'updatedAt' },
OwnerId:
{ name: 'OwnerId',
type: [Object],
allowNull: true,
references: [Object],
onDelete: 'SET NULL',
onUpdate: 'CASCADE',
Model: Board,
fieldName: 'OwnerId',
_modelAttribute: true,
field: 'OwnerId' } },
attributes: [ 'id', 'name', 'favorited', 'createdAt', 'updatedAt', 'OwnerId' ],
_isAttribute: { [Function: memoized] cache: MapCache { size: 0, __data__: [Object] } },
getUser: [Function],
setUser: [Function],
createUser: [Function],
getUsers: [Function],
countUsers: [Function],
hasUser: [Function],
hasUsers: [Function],
setUsers: [Function],
addUser: [Function],
addUsers: [Function],
removeUser: [Function],
removeUsers: [Function] }
But when I try to runt he following in my routes it says that addUser/addUsers are not functions:
router.get("/boards/:id/users/:uid", function(req, res) {
var query = {};
if (req.params.id) {
query.id = req.params.id;
db.Board.findAll({
where: query
}).then(function(dbBoard) {
dbBoard.addUser(req.params.uid);
res.json(dbBoards);
});
}
});
Any help would be appreciated. Thank you.
Adding an association is instance specific. You have to call this method on a single instance of the Board model. For example:
const singleBoard = db.board.findOne({where: query};
const user = db.users.findOne({where: {id: parseInt(req.params.uid)});
singleBoard.addUser(user);
You can iterate through the array you get from querying through findAll if you have to do the same operation on multiple boards.
https://sequelize.org/master/manual/advanced-many-to-many.html