Postgres Include throws "name": "SequelizeEagerLoadingError" - postgresql

I am having trouble figuring out why my include statement throws this error
"name": "SequelizeEagerLoadingError"
My controller function in question looks like this
retrieve (req, res) {
return User
.findOne({
where: {
token_id: req.params.token_id
},
include: [{
model: subscribedcurrency,
as: 'subscribed currency'
}]
})
.then(user => {
if (!user) {
return res.status(404).send({
message: 'User Not Found'
})
}
return res.status(200).send(user)
})
.catch(error => res.status(400).send(error))
},
The model for subscribedcurrency looks like this
module.exports = (sequelize, DataTypes) => {
var SubscribedCurrency = sequelize.define('SubscribedCurrency', {
symbol: {
type: DataTypes.STRING,
allowNull: false
},
name: {
type: DataTypes.STRING,
allowNull: false
},
priceAtSubscription: {
type: DataTypes.STRING,
allowNull: false
}
})
SubscribedCurrency.associate = (models) => {
SubscribedCurrency.hasMany(models.User, {
foreignKey: 'userId',
onDelete: 'CASCADE'
})
}
return SubscribedCurrency
}
I've tried changing how it queries, the primary key of User, just about everything I can think of. The relationship between User and subscribedCurrency is many to many.
without the include statement and the findOne query works perfect fine!

I think all you need is to remove as: 'subscribed currency' , as you haven't define association with alias.
So From this :
include: [{
model: subscribedcurrency,
as: 'subscribed currency'
}]
To this :
include: [{
model: subscribedcurrency,
}]

Related

Sequelize how to structure the chat part of the app?

I currently have an app where users can log in and make posts. Next to each post, there is button that if pressed, the current user can send a message to the user who created the post.
I have a posts model and a user model. Each user can post as many posts as they want, but each post only belongs to one user.
const User = db.define(
"User",
{
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true,
},
name: {
type: Sequelize.STRING,
},
email: {
type: Sequelize.STRING,
},
password: {
type: Sequelize.STRING,
},
},
);
const Post = db.define(
"Post",
{
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true,
},
title: {
type: Sequelize.STRING,
},
subTitle: {
type: Sequelize.STRING,
},
userId: {
type: Sequelize.INTEGER,
},
},
);
User.hasMany(Post,
{ foreignKey: "userId" }
);
Post.belongsTo(User,
{ foreignKey: "userId" }
);
What i am trying to implement now is the messaging functionality.
Here is my messages Model so far:
const Messages = db.define("Messages", {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER,
},
senderId: {
type: Sequelize.STRING,
},
receiverId: {
type: Sequelize.STRING,
},
message: {
type: Sequelize.STRING,
},
});
User.hasMany(Messages, {
foreignKey: 'senderId'
});
User.hasMany(Messages, {
foreignKey: 'receiverId'
});
Messages.associate = (models) => {
Messages.belongsTo(models.Post, {
foreignKey: "PostId",
});
Messages.belongsTo(models.User, {
foreignKey: "senderId",
});
Messages.belongsTo(models.User, {
foreignKey: "receiverId",
});
};
User.hasMany(Conversations, {
foreignKey: 'conversationId'
});
And here is my conversation model so far:
const Conversations = db.define("Conversations", {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER,
}
});
module.exports = Conversations;
Conversations.associate = (models) => {
Conversations.hasMany(models.Message);
models.Message.belongsTo(Conversations);
};
Also, the message can be sent from 1 user to another not to a group of users.
Are my associations and table structures correct?
Update
User.hasMany(Messages, {
foreignKey: 'senderId'
});
User.hasMany(Messages, {
foreignKey: 'receiverId'
});
Messages.associate = (models) => {
Messages.belongsTo(models.User, {
foreignKey: "senderId",
});
Messages.belongsTo(models.User, {
foreignKey: "receiverId",
});
Messages.belongsTo(models.Conversations,{
foreignKey: "conversationId",
});
};
Conversations.associate = (models) => {
Conversations.hasMany(models.Messages,{
foreignKey: "conversationId",
});
Conversations.belongsTo(models.Post,{
foreignKey: "PostId",
})
};
Post.hasMany(Conversations, { foreignKey: "PostId" });
User.hasMany(Conversations, {
foreignKey: 'user1'
});
User.hasMany(Conversations, {
foreignKey: 'user2'
});
Using this implementation when i try sending a message, the conversationId in the messages table is null.
Here is my post request:
router.post("/",
auth,
async (req, res) => {
const post = await Post.findOne({where:{id:req.body.postId}})
if (!post) return res.status(400).send({ error: "Invalid postId." });
const targetUser = await User.findOne({where:{post.userId}})
if (!targetUser) return res.status(400).send({ error: "Invalid
userId." });
await Conversations.findOne({
where:{
user1:{[Op.or]:[req.user.id,post.userId]},
user2:{[Op.or]:[req.user.id,post.userId]},
PostId:req.body.postId,
}
}).then(conversation=>{
if(conversation){
return conversation
}else{
return Conversations.create({
user1: req.user.id,
user2: post.userId,
PostId:req.body.postId,
})
}
}
)
Messages.create({
senderId: req.user.id,
receiverId: post.userId,
message: req.body.message,
conversationId:conversation.id //MY PROBLEM IS HERE
})
.then(
res.status(201).send({
msg: "upload successful",
}));
const { expoPushToken } = targetUser;
if (Expo.isExpoPushToken(expoPushToken))
await sendPushNotification(expoPushToken, message);
});
All models look good. The issues are with associations.
If you define more then one association between the same two models you should indicate different aliases to distinguish them from each other in queries.
User.hasMany(Messages, {
foreignKey: 'senderId',
as: 'OutgoingMessages'
});
User.hasMany(Messages, {
foreignKey: 'receiverId',
as: 'IncomingMessages'
});
Messages.belongsTo(models.User, {
foreignKey: "senderId",
as: 'Sender'
});
Messages.belongsTo(models.User, {
foreignKey: "receiverId",
as: 'Receiver'
});
Also it's better to define associations in the same manner either directly after model definition or in a static method like associate. The latter approach is preferable because it allows to define each model in its own module without any cross-references using the models parameter in associate method to access other models that should be associated with a given model.
Last note: try to define associations where a model on the left side of an association definition in its own associate method.
It means that
models.Message.belongsTo(Conversations);
should be in Message model associate method:
Message.belongsTo(models.Conversations);
That way you always know where to find all associations that define links from a certain model to other models.
UPDATE
You should store a found or a created conversation to a variable in order to use it while creating a message:
let conversation = await Conversations.findOne({
where:{
user1:{[Op.or]:[req.user.id,post.userId]},
user2:{[Op.or]:[req.user.id,post.userId]},
PostId:req.body.postId,
}
})
if (!conversation){
conversation = await Conversations.create({
user1: req.user.id,
user2: post.userId,
PostId:req.body.postId,
})
}
const newMessage = await Messages.create({
senderId: req.user.id,
receiverId: post.userId,
message: req.body.message,
conversationId:conversation.id
})
res.status(201).send({
msg: "upload successful",
});
Don't try to mix up then/catch and await. If you use await you will already have a result or an exception (which you can handle using try/catch).

How do I get sequelize to not use timestamp fields when generating SQL for getting n:m associated models?

My models:
'use strict';
module.exports = (sequelize, DataTypes) => {
const Vendor = sequelize.define('Vendor', {
id: { type: DataTypes.INTEGER, allowNull: false, autoIncrement: true, primaryKey: true },
// other fields
}, {});
Vendor.associate = function (models) {
Vendor.belongsToMany(models.Corporate, { through: 'VendorCorporates', foreignKey: 'vendorId' });
};
return Vendor;
};
and
'use strict';
module.exports = (sequelize, DataTypes) => {
const Corporate = sequelize.define('Corporate', {
id: { type: DataTypes.INTEGER, allowNull: false, autoIncrement: true, primaryKey: true },
// other fields
}, {});
Corporate.associate = function (models) {
Corporate.belongsToMany(models.Vendor, { through: 'VendorCorporates', foreignKey: 'corporateId' });
};
return Corporate;
};
and the migration for the association (link) table:
'use strict';
const vendorCorps = 'VendorCorporates', vendorId = 'vendorId', corpId = 'corporateId';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable(vendorCorps, {
vendorId: {
type: Sequelize.INTEGER,
references: {
model: 'Vendors',
key: 'id'
},
onUpdate: 'CASCADE',
onDelete: 'CASCADE'
},
corporateId: {
type: Sequelize.INTEGER,
references: {
model: 'Corporates',
key: 'id'
},
onUpdate: 'CASCADE',
onDelete: 'CASCADE'
},
// notice no timestamps
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('VendorCorporates');
}
};
Now when I try to get the Corporates that are associated with a Vendor:
let vendor = await models.Vendor.findByPk(pk);
let corporates = await vendor.getCorporates();
the query fails because sequelize generates SQL with the fields updatedAt and createdAt. How do I tell sequelize to generate SQL without the timestamp fields?
Clarification: I want timestamps in the Corporate & Vendor models, but none in the link/association table, and I want the join query (vendor.getCorporates()) to be generated accordingly.
You can indicate this using the timestamps option. For instance:
const Corporate = sequelize.define('Corporate', {
id: { type: DataTypes.INTEGER, allowNull: false, autoIncrement: true, primaryKey: true },
// other fields
}, {
timestamps: false
});
Finally solved this by writing a model for the n:m association itself:
'use strict';
module.exports = (sequelize, DataTypes) => {
const VendorCorporate = sequelize.define('VendorCorporate', {
vendorId: DataTypes.INTEGER,
corporateId: DataTypes.INTEGER
}, { timestamps: false });
VendorCorporate.associate = function (models) {
// associations can be defined here
};
return VendorCorporate;
};
and changing:
// db/models/vendor.js
Vendor.belongsToMany(models.Corporate, { through: 'VendorCorporates', foreignKey: 'vendorId' });
to:
Vendor.belongsToMany(models.Corporate, { through: models.VendorCorporate, foreignKey: 'vendorId' });
and making a similar change in the Corporate model file.

Sequelize relation "likes" does not exist

I'm not understanding why sequelize is giving me this error.
relation "Likes" does not exist
I referenced a similar question, but it didn't provide me with much of an insight:
Sequelize Error: Relation does not exist
and this too:
Sequelize Migration: relation <table> does not exist
My table names matches the reference model names.
I don't think it has anything to do with the models, but everything to do with the migrations file.
This is what I have
Posts migration
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Posts', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
title: {
type: Sequelize.STRING
},
post_content: {
type: Sequelize.STRING
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
},
userId: {
type: Sequelize.INTEGER,
references: {
model: 'Users',
key: 'id'
}
},
likeId: {
type: Sequelize.INTEGER,
onDelete: 'CASCADE',
references: {
model: 'Likes',
key: 'id'
}
},
username: {
type: Sequelize.STRING
},
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Posts');
}
};
Likes migration
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Likes', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
like: {
type: Sequelize.BOOLEAN
},
userId: {
type: Sequelize.INTEGER,
references: {
model: 'Users',
key: 'id',
as: 'userId'
}
},
postId: {
type: Sequelize.INTEGER,
references: {
model: 'Posts',
key: 'id',
as: 'postId'
}
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Likes');
}
};
models/like.js
'use strict';
const Like = (sequelize, DataTypes) => {
const Likes = sequelize.define('Likes', {
like:{
type:DataTypes.BOOLEAN,
allowNull:true
}
}, {});
Likes.associate = function(models) {
Likes.belongsTo(models.User, {
onDelete: "CASCADE",
foreignKey: {
foreignKey: 'userId'
}
})
Likes.belongsTo(models.Post, {
onDelete: 'CASCADE',
foreignKey: 'likeId',
targetKey: 'id',
})
}
return Likes;
};
module.exports = Like;
models/post.js
module.exports = (sequelize, DataTypes) => {
const Post = sequelize.define('Post', {
title: DataTypes.STRING,
post_content: DataTypes.STRING,
username: DataTypes.STRING
}, {});
Post.associate = function(models) {
Post.belongsTo(models.User, { foreignKey: 'userId', targetKey: 'id' });
Post.belongsTo(models.Likes, { foreignKey: 'likeId', targetKey: 'id' });
};
return Post;
};
models/user.js
'use strict';
const User = (sequelize, DataTypes) => {
const myUser = sequelize.define('User', {
username: DataTypes.STRING,
email: DataTypes.STRING,
password: DataTypes.STRING,
resetPasswordToken:DataTypes.STRING,
resetPasswordExpires: DataTypes.DATE
}, {});
myUser.associate = function(models) {
myUser.hasMany(models.Post, { foreignKey: 'userId', as:'users' });
myUser.hasMany(models.Likes, { foreignKey: 'userId', as:'likes' });
};
return myUser;
};
module.exports = User;
Instead of adding likeId in the migration. I needed to add a new migration like so
sequelize migration:generate --name add_likeId_to_posts
so we have now
'use strict';
module.exports = {
up: function (queryInterface, Sequelize) {
return queryInterface.addColumn(
'Posts',
'likeId',
{
type: Sequelize.INTEGER,
allowNull: false,
references: {
model: 'Likes',
key: 'id'
}
}
)
},
down: function (queryInterface, Sequelize) {
return queryInterface.removeColumn(
'Posts',
'likeId'
)
}
};
which gives us
voila!
and likeId is Associated on the table

PostgreSQL - SequelizeDatabaseError with join table (Code: 42P01)

I am trying to create a join table for many to many relationships.
I am getting the following error:
"name": "SequelizeDatabaseError",
"parent": {
"name": "error",
"length": 110,
"severity": "ERROR",
"code": "42P01",
"position": "13",
"file": "parse_relation.c",
"line": "1160",
"routine": "parserOpenTable",
"sql": "INSERT INTO \"user_routes\" (\"id\",\"userId\",\"routeId\",\"createdAt\",\"updatedAt\") VALUES (DEFAULT,'1','1','2017-11-15 03:57:21.791 +00:00','2017-11-15 03:57:21.791 +00:00') RETURNING *;"
},
The relationship is between User and Route:
module.exports = (sequelize, DataTypes) => {
const Route = sequelize.define("Route", {
open: {
type: DataTypes.BOOLEAN,
allowNull: true
}
});
Route.associate = models => {
Route.belongsToMany(models.User, {
through: "userRoutes",
as: "users"
});
};
return Route;
};
module.exports = (sequelize, DataTypes) => {
var User = sequelize.define("User", {
email: DataTypes.TEXT,
password: DataTypes.TEXT
});
User.associate = models => {
User.belongsToMany(models.Route, {
through: "userRoutes",
as: "routes"
});
};
return User;
};
Migration files for user and route does not have much but just the basics. For join table:
"use strict";
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable("user_route", {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
},
userId: {
type: Sequelize.INTEGER,
onDelete: "CASCADE",
references: {
model: "Users",
key: "id"
}
},
routeId: {
type: Sequelize.INTEGER,
onDelete: "CASCADE",
references: {
model: "Routes",
key: "id"
}
}
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable("user_route");
}
};
My controller and route is as following:
create(req, res) {
return UserRoutes.create({
userId: req.body.userId,
routeId: req.body.routeId
})
.then(userRoute => res.status(201).send(userRoute))
.catch(err => res.status(400).send(err));
},
app.post("/api/userRoutes", userRoutesController.create);
So when I try to post to that route, I get the error message on the top of the post.
I had this same error, and was similarly thrown by an unrelated issue (my SQL clause had single quotes that were all escaped, and, since the query ran when I removed them, I was focused on a way to remove the escaping). My problem was that my migration was generated with a table name that violated the PostgreSQL naming convention; when I corrected this (I had a capitalized first letter in my table name, so I edited the migration for a lowercase first letter and reran it), the error disappeared. Maybe that might fix yours?
After you created db, models and migrations, you need to run
sequelize db:migrate
before running your application.
If not, it will throw SequelizeDatabaseError as mentioned above.
If you look at the line:
"routine": "parserOpenTable"
We will see an error when opening the table.
Most likely, the error is due to the naming of the tables.
Pay attention to capital letters.
Example,
"./models/user.js": tableName "users", but in DBMS we have table "Users".
Just change tableName.

Eager Loading : How to disable specific fields of included table

I am trying to do Eager Loading in Sequelize with PostgreSQL where I need to find the Users which have a given specific Mail id or basically, i am performing find operation on Mail model while using include to include User model
UserModel :
module.exports = function (sequelize, Sequelize) {
var User = sequelize.define('User', {
userId: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
firstname: {
type: Sequelize.STRING,
require: true
},
lastname: {
type: Sequelize.STRING,
require: true
},
age: {
type: Sequelize.INTEGER,
require: true
},
phone: {
type: Sequelize.STRING,
require: true
},
location: {
type: Sequelize.STRING,
require: true
},
createdAt: {
type: Sequelize.DATE,
defaultValue: Sequelize.NOW
},
updatedAt: {
type: Sequelize.DATE,
defaultValue: Sequelize.NOW
}
});
return User;
};
MailModel :
module.exports = function (sequelize, Sequelize) {
var User = require('./User.js')(sequelize, Sequelize)
var Mail = sequelize.define('Mail', {
name: {
type: Sequelize.STRING,
require: true
}
});
Mail.belongsTo(User);
return Mail;
};
MailController :
var db = require('../services/db.js');
module.exports = {
create: function (req, res, next) {
var Mailm = db.MailModel;
var name = req.body;
try {
db.sequelize.sync().then(function () {
Mailm.create(name).then(function (found) {
return res.json({
success: true,
message: found.get({
plain: true
})
});
})
});
} catch (ex) {
res.json({
success: false,
exception: ex
});
return;
}
},
query: function (req, res, next) {
var Mailm = db.MailModel;
var Userm = db.UserModel;
var name = req.body;
var option = {};
option.where = name;
option.include = [{
model: Userm
}];
try {
Mailm.findAll(option).then(function (found) {
console.log(found);
return res.json({
success: true,
message: found
});
});
} catch (ex) {
res.json({
success: false,
exception: ex
});
return;
}
}
};
It is returning me the records of both User and Mail table in exactly the right way .
Output :
{
"success": true,
"message":[
{
"id": 2,
"name": "Mailb2",
"createdAt": "2015-07-30T07:32:51.807Z",
"updatedAt": "2015-07-30T07:32:51.807Z",
"UserUserId": 2,
"User":{
"userId": 2,
"firstname": "Prerna",
"lastname": "Jain",
"age": 20,
"phone": "9812123456",
"location": "Sirsa",
"createdAt": "2015-07-30T07:30:48.000Z",
"updatedAt": "2015-07-30T07:30:48.000Z"
}
}
]
}
But I want to disable createdAt and updatedAt fields of User table so that it does not give me these two fields in the output for User.
I have tried a lot as of how to do this but still in vain.Can anyone please help me out.
I bet this is coming late, add attribute/properties to your models called timestamps, it accepts a boolean as a value. For example:
module.exports = function (sequelize, Sequelize) {
var User = require('./User.js')(sequelize, Sequelize)
var Mail = sequelize.define('Mail', {
name: {
type: Sequelize.STRING,
require: true
}
},
{
// This does the magic
timestamps: false,
});
Mail.belongsTo(User);
return Mail;
};
Also, add it to the User model:
var User = sequelize.define('User', {
userId: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
firstname: {
type: Sequelize.STRING,
require: true
},
lastname: {
type: Sequelize.STRING,
require: true
},
age: {
type: Sequelize.INTEGER,
require: true
},
phone: {
type: Sequelize.STRING,
require: true
},
location: {
type: Sequelize.STRING,
require: true
},
createdAt: {
type: Sequelize.DATE,
defaultValue: Sequelize.NOW
},
updatedAt: {
type: Sequelize.DATE,
defaultValue: Sequelize.NOW
}
},
{
timestamps: false
});
return User;
};
You can use
Model.findAll({
attributes: { exclude: ['baz'] }
});
more examples with attributes - http://docs.sequelizejs.com/en/latest/docs/querying/#attributes