onDelete cascade not working on "one to one" - Sequelize.js - mysql

My category table has a foreign key: name_id which is referenced to id of translation table, now category and translation have one to one relationship.
I would like to add a onDelete CASCADE so when I delete category the translation would also be deleted.
const Translation = sequelize.define('Translation',
{
id: {
type: DataTypes.INTEGER,
allowNull: false,
primaryKey: true,
autoIncrement: true
},
en: DataTypes.STRING(1000),
es: DataTypes.STRING(1000),
pt: DataTypes.STRING(1000)
}
)
const Category = sequelize.define('Category',
{
id: {
type: DataTypes.INTEGER,
allowNull: false,
primaryKey: true,
autoIncrement: true
},
nameId: DataTypes.INTEGER
}
)
Category.belongsTo(Translation, { as: 'name', foreignKey: 'nameId', onDelete: 'cascade' })
Translation.hasOne(Category, { foreignKey: 'nameId', onDelete: 'cascade' })
Is there something I am missing?
P.S. other tables will also be related to translations, that´s why foreign key is placed in category and not the other way around.

Your association is on the wrong side! With such association definition, you will get the following scheme:
+----------+
| Category |
+----------+
| id |
+----------+
| nameId |
+----------+
+-------------+
| Translation |
+-------------+
| id |
+-------------+
| en |
+-------------+
| es |
+-------------+
| pt |
+-------------+
| nameId | <-- references Category.id
+-------------+
Therefore, your cascade is working, but it will delete orphaned Categories when a Translation is dropped. To achieve what you seek, you must define your association like so:
Category.hasOne(Translation, { as: 'name', foreignKey: 'nameId', onDelete: 'cascade' })
Translation.belongsTo(Category, { foreignKey: 'nameId', onDelete: 'cascade' })
Edit: If you have multiple tables with translations, you need to drop the belongTo associations. You will only have the hasOne on each of these tables, ex:
Category.hasOne(Translation, { as: 'name', foreignKey: 'nameId', onDelete: 'cascade' });
Event.hasOne(Translation, { as: 'description', foreignKey: 'descriptionId', onDelete: 'cascade' });

Related

How to properly make a table association using hasOne, belongsTo using Sequelize and express js

I am working on expre6ssjs with sequelize version 6.
I have User and Message and I need to make one to many relationship between them.
here is what I have so far
import { Sequelize, DataTypes } from "sequelize";
const sequelize = new Sequelize(
'messagenger',
'mysql',
'password',
{
host: 'localhost',
dialect: 'mysql'
}
);
sequelize.authenticate().then(() => {
console.log('Connection has been established successfully.');
}).catch((error) => {
console.error('Unable to connect to the database: ', error);
});
const User = sequelize.define("users", {
name: {
type: DataTypes.STRING,
allowNull: false
}
});
const Message = sequelize.define("messages", {
text: {
type: DataTypes.STRING,
allowNull: false
},
});
sequelize.sync({ force: true }).then(() => {
// User.hasMany(Message, {
// foreignKey: "user_id",
// as: "messages"
// });
// Message.belongsTo(User, {
// foreignKey: "user_id",
// as: "users"
// });
// Message.belongsTo(User, {
// foreignKey: "userId",
// as: "user"
// })
Message.belongsTo(User);
User.hasMany(Message);
}).catch((error) => {
console.error('Unable to create table : ', error);
});
this is my messages table.
mysql> describe messages;
+-----------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| text | varchar(255) | NO | | NULL | |
| createdAt | datetime | NO | | NULL | |
| updatedAt | datetime | NO | | NULL | |
+-----------+--------------+------+-----+---------+----------------+
4 rows in set (0.00 sec)
I have tried a couple of versions as you can see in the comments. but nothing worked.
https://sebhastian.com/sequelize-hasone/
I was following this tutorial and this does not look complicated at all.
By the way, I have been searching for the answer for a while now. And I know people also use a different approach such as referenced in the table definition.
But my goal is to use hasMany, belongsTo, these methods as they are very explicit.
Any advice will help me a lot.
Thank you in advance.
You need to define all models and associations BEFORE calling sync in order to Sequelize to know all about what tables and foreign key fields and foreign key constraints it needs to create.
const User = sequelize.define("users", {
name: {
type: DataTypes.STRING,
allowNull: false
}
});
const Message = sequelize.define("messages", {
text: {
type: DataTypes.STRING,
allowNull: false
},
});
Message.belongsTo(User);
User.hasMany(Message);
sequelize.sync({ force: true }).then(() => {
console.info('Model synchronization completed');
}).catch((error) => {
console.error('Unable to create table : ', error);
});

Sequelize join two columns of one table to the same column of another table

I have a table Relation with columns userId1 and userId2 it basically stores the relation between two users, userId1 and userId2 is foreign key here referenced from the User tables id (PK) column.
id: {
type: DataTypes.INTEGER.UNSIGNED,
autoIncrement: true,
primaryKey: true,
allowNull: false,
},
userId1: {
type: DataTypes.INTEGER.UNSIGNED,
allowNull: false,
},
userId2: {
type: DataTypes.INTEGER.UNSIGNED,
allowNull: false,
},
status: {
type: DataTypes.ENUM,
},
Then there is another table Posts containing information about posts.
id: {
type: DataTypes.INTEGER.UNSIGNED,
autoIncrement: true,
primaryKey: true,
allowNull: false,
},
content: {
type: DataTypes.TEXT,
allowNull: false,
},
postedBy: {
type: DataTypes.INTEGER.UNSIGNED,
allowNull: false,
},
I want to get lists of post of only those user who have a relation with me like friends, means my id lets suppose is 1 and it is in userId1 column and userId2 column has id in it, then I want to fetch all posts of value 2 from posts postedBy column.
This case can be vice versa as my id can be in userId2 column and I need to get all posts of the user whose value is in userId1 column.
I have read through all the questions and answers like multiple associations but it is just not working for me.
This is my associations in Posts model
Posts.hasOne(RelationModel, {
foreignKey: 'userId1',
sourceKey: 'postedBy',
as: 'first',
})
Posts.hasOne(RelationModel, {
foreignKey: 'userId2',
sourceKey: 'postedBy',
as: 'second',
})
Below is my include array.
include:[
{
model: RelationModel,
as: 'first',
where: {
status: 'accepted',
[Op.or]: [
{ userId1: request.user.id },
{ userId2: request.user.id },
],
},
},
{
model: RelationModel,
as: 'second',
where: {
status: 'accepted',
[Op.or]: [
{ userId1: request.user.id },
{ userId2: request.user.id },
],
},
}
]
The query being generated from this is below, where 151 is the logged in user id, means my id
SELECT
`posts`.*,
`first`.*,
`second`.*
FROM
`posts` AS `posts`
INNER JOIN
`relations` AS `first` ON `posts`.`postedBy` = `first`.`userId1`
AND (`first`.`userId1` = 151
OR `first`.`userId2` = 151)
AND `first`.`status` = 'accepted'
INNER JOIN
`relations` AS `second` ON `posts`.`postedBy` = `second`.`userId2`
AND (`second`.`userId1` = 151
OR `second`.`userId2` = 151)
AND `second`.`status` = 'accepted'
WHERE
`posts`.`deletedAt` IS NULL
ORDER BY `posts`.`id` ASC , `posts`.`id` ASC;
But the query I want to build is below
SELECT
`posts`.*,
`first`.*
FROM
`posts` AS `posts`
INNER JOIN
`relations` AS `first` ON (`posts`.`postedBy` = `first`.`userId2`
OR `posts`.`postedBy` = `first`.`userId1`)
AND (`first`.`userId1` = 151
OR `first`.`userId2` = 151)
AND `first`.`isFriend` = TRUE
AND `first`.`status` = 'accepted'
WHERE
`posts`.`deletedAt` IS NULL
ORDER BY `posts`.`id` ASC , `posts`.`id` ASC;
How to construct this query in sequelize?
You need to specify a unique as keyword for each relationship in the association as well as on the include for your query.
Posts.hasOne(RelationModel, {
foreignKey: 'userId1',
sourceKey: 'postedBy',
as: 'first',
});
Posts.hasOne(RelationModel, {
foreignKey: 'userId2',
sourceKey: 'postedBy',
as: 'second',
});
Then when you query you specify the unique as that identifies the relationship for the join
Post.findByPk(id, {
include: [{
model: RelationModel,
as: 'first',
},
{
model: RelationModel,
as: 'second',
}],
});

error in sequelize > "name": "SequelizeEagerLoadingError"

I have two tables:
//User.js
module.exports = (sequelize, DataTypes) => {
const User = sequelize.define("User", {
userId: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
primaryKey: true,
},
email: {
type: DataTypes.STRING,
},
firstName: {
type: DataTypes.STRING,
allowNull: false,
},
lastName: {
type: DataTypes.STRING,
defaultValue: "",
},
password: {
type: DataTypes.STRING,
allowNull: false,
},
chapterId: {
type: DataTypes.STRING,
},
});
User.associate = (models) => {
User.belongsTo(models.Chapter, {
foreignKey: "chapterId",
targetKey: "chapterId",
as: "chapter",
});
};
return User;
};
and
//chapter table
module.exports = (sequelize, DataTypes) => {
const Chapter = sequelize.define("Chapter", {
chapterId: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
primaryKey: true,
},
chapterName: {
type: DataTypes.STRING,
allowNull: false,
},
isChapterLocal: {
type: DataTypes.BOOLEAN,
allowNull: false,
},
});
Chapter.associate = (models) => {
};
return Chapter;
};
and i am trying to fetch users with chapters included into it.
let getAll = async (req, res) => {
try {
const userData = await db.User.findAll({
include: [
{
model: Chapter,
as: "chapter",
},
],
});
res.send(userData);
} catch (e) {
res.send(e);
}
};
how to include chapter id and chapter name from chapter table, as present in chapterId row for user table.
I am new to sequelize and MySQL and am unsure if the relation i have defined in the user model is good.
Do we need to define associations in both tables.
It should work as expected. E.g.
import { sequelize } from '../../db';
import { Model, DataTypes } from 'sequelize';
class User extends Model {}
User.init(
{
userId: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
primaryKey: true,
},
email: {
type: DataTypes.STRING,
},
firstName: {
type: DataTypes.STRING,
allowNull: false,
},
lastName: {
type: DataTypes.STRING,
defaultValue: '',
},
password: {
type: DataTypes.STRING,
allowNull: false,
},
chapterId: {
type: DataTypes.STRING,
},
},
{ sequelize, modelName: 'User' },
);
class Chapter extends Model {}
Chapter.init(
{
chapterId: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
primaryKey: true,
},
chapterName: {
type: DataTypes.STRING,
allowNull: false,
},
isChapterLocal: {
type: DataTypes.BOOLEAN,
allowNull: false,
},
},
{ sequelize, modelName: 'Chapter' },
);
User.belongsTo(Chapter, { foreignKey: 'chapterId', targetKey: 'chapterId', as: 'chapter' });
(async function test() {
try {
await sequelize.sync({ force: true });
// seed
await User.create(
{
userId: '1',
email: 'example#gmail.com',
firstName: 'Lin',
lastName: 'Du',
password: '123',
chapter: {
chapterId: '1',
chapterName: 'ok',
isChapterLocal: false,
},
},
{ include: [{ model: Chapter, as: 'chapter' }] },
);
// test
const userData = await User.findAll({
include: [{ model: Chapter, as: 'chapter' }],
raw: true,
});
console.log('userData:', userData);
} catch (error) {
console.log(error);
} finally {
sequelize.close();
}
})();
The execution results:
Executing (default): DROP TABLE IF EXISTS "User" CASCADE;
Executing (default): DROP TABLE IF EXISTS "Chapter" CASCADE;
Executing (default): DROP TABLE IF EXISTS "Chapter" CASCADE;
Executing (default): CREATE TABLE IF NOT EXISTS "Chapter" ("chapterId" VARCHAR(255) NOT NULL UNIQUE , "chapterName" VARCHAR(255) NOT NULL, "isChapterLocal" BOOLEAN NOT NULL, PRIMARY KEY ("chapterId"));
Executing (default): SELECT i.relname AS name, ix.indisprimary AS primary, ix.indisunique AS unique, ix.indkey AS indkey, array_agg(a.attnum) as column_indexes, array_agg(a.attname) AS column_names, pg_get_indexdef(ix.indexrelid) AS definition FROM pg_class t, pg_class i, pg_index ix, pg_attribute a WHERE t.oid = ix.indrelid AND i.oid = ix.indexrelid AND a.attrelid = t.oid AND t.relkind = 'r' and t.relname = 'Chapter' GROUP BY i.relname, ix.indexrelid, ix.indisprimary, ix.indisunique, ix.indkey ORDER BY i.relname;
Executing (default): DROP TABLE IF EXISTS "User" CASCADE;
Executing (default): CREATE TABLE IF NOT EXISTS "User" ("userId" VARCHAR(255) NOT NULL UNIQUE , "email" VARCHAR(255), "firstName" VARCHAR(255) NOT NULL, "lastName" VARCHAR(255) DEFAULT '', "password" VARCHAR(255) NOT NULL, "chapterId" VARCHAR(255) REFERENCES "Chapter" ("chapterId") ON DELETE NO ACTION ON UPDATE CASCADE, PRIMARY KEY ("userId"));
Executing (default): SELECT i.relname AS name, ix.indisprimary AS primary, ix.indisunique AS unique, ix.indkey AS indkey, array_agg(a.attnum) as column_indexes, array_agg(a.attname) AS column_names, pg_get_indexdef(ix.indexrelid) AS definition FROM pg_class t, pg_class i, pg_index ix, pg_attribute a WHERE t.oid = ix.indrelid AND i.oid = ix.indexrelid AND a.attrelid = t.oid AND t.relkind = 'r' and t.relname = 'User' GROUP BY i.relname, ix.indexrelid, ix.indisprimary, ix.indisunique, ix.indkey ORDER BY i.relname;
Executing (default): INSERT INTO "Chapter" ("chapterId","chapterName","isChapterLocal") VALUES ($1,$2,$3) RETURNING *;
Executing (default): INSERT INTO "User" ("userId","email","firstName","lastName","password","chapterId") VALUES ($1,$2,$3,$4,$5,$6) RETURNING *;
Executing (default): SELECT "User"."userId", "User"."email", "User"."firstName", "User"."lastName", "User"."password", "User"."chapterId", "chapter"."chapterId" AS "chapter.chapterId", "chapter"."chapterName" AS "chapter.chapterName", "chapter"."isChapterLocal" AS "chapter.isChapterLocal" FROM "User" AS "User" LEFT OUTER JOIN "Chapter" AS "chapter" ON "User"."chapterId" = "chapter"."chapterId";
userData: [ { userId: '1',
email: 'example#gmail.com',
firstName: 'Lin',
lastName: 'Du',
password: '123',
chapterId: '1',
'chapter.chapterId': '1',
'chapter.chapterName': 'ok',
'chapter.isChapterLocal': false } ]
Check the database:
node-sequelize-examples=# select * from "User";
userId | email | firstName | lastName | password | chapterId
--------+-------------------+-----------+----------+----------+-----------
1 | example#gmail.com | Lin | Du | 123 | 1
(1 row)
node-sequelize-examples=# select * from "Chapter";
chapterId | chapterName | isChapterLocal
-----------+-------------+----------------
1 | ok | f
(1 row)

Associating a reference table in sequelize

I have a couple of tables I'm trying to associate in Sequelize -- a jobaids_sections_messages table containing messages a user enters, and a jobaids_sections_messages_levels table which is a static reference used by the messages table. They're set up like so
this.jobaidMessage = sequelize.define('jobaids_sections_messages', {
message: Sequelize.STRING,
attuid: Sequelize.STRING,
level: Sequelize.INTEGER
}, {
paranoid: true
});
this.jobaidMessageLevel = sequelize.define('jobaids_sections_messages_levels', {
name: Sequelize.STRING
}, {
timestamps: false
});
The jobaids_sections_messages_levels table is set up like so:
| id | name |
| --- | -------- |
| 1 | Critical |
| 2 | Major |
| 3 | Warning |
| 4 | Info |
I want to make is so that when I create a new message, I can pass the level in as a key to the jobaids_sections_messages_levels table, and upon retrieving a message, I get the level back as
{
...
level: {
id: 2,
name: 'Major'
}
}
How should I set my associations up here? So far, I have
this.jobaidMessageLevel.belongsTo(this.jobaidMessage, {
foreignKey: 'level'
});
Though I'm not sure about the reversal of this association. Would it be a "many-to-one" relationship of some sorts?
Thank you!
Your message has a single level and technically your levels can have many messages. So simply stating that your message hasOne level will do the association needed. Then when you pull down a message and include the level, it will come back.
this.jobaidMessage.hasOne(this.jobaidMessageLevel, {
foreignKey: 'levelId'
});

How to join twice a single table in sequelize using following and followers example

This code is running perfectly. i can get following and followers now i need to see that followers that i am getting is i am also following them ?
This is the question that how can i make another query/subQuery to Followers table and see that i am also following to my followers.
Follower Table
export default function(sequelize, DataTypes) {
return sequelize.define('Follower', {
_id: {
type: DataTypes.INTEGER,
allowNull: false,
primaryKey: true,
autoIncrement: true
},
userId: {
type: DataTypes.INTEGER,
allowNull: false
},
followingId: {
type: DataTypes.INTEGER,
allowNull: false
}
});
}
Association
db.Follower.belongsTo(db.User, {as:'following', foreignKey: 'followingId'});
db.Follower.belongsTo(db.User, {as:'follower', foreignKey: 'userId'});
Query
Follower.findAll({
where: {
followingId: userId
},
attributes: ['_id'],
include: [
{
model: User,
attributes: ['fullName', 'username', '_id', 'picture'],
as: 'follower'
}
]
})
UPDATE
I have achieve desired result form row query :
SELECT F.userId, F.`followingId` , F1.`followingId` as IsFollowing , U.`fullName` FROM Followers as F
INNER JOIN Users as U ON userId = U._id
LEFT JOIN Followers as F1 On F.userId = F1.followingId
WHERE F.followingId = 142
Still struggling in sequelize.
to transform your row query into sequlize request try this:
Association
db.Follower.belongsTo(db.User, {as: 'following', foreignKey: 'followingId', sourceKey: 'userId'});
db.Follower.hasOne(db.User, {as: 'follower', foreignKey: 'userId'});
Query
Follower.findAll({
where: {
followingId: userId
},
attributes: ['userId', 'followingId'],
include: [
{
model: User,
attributes: ['fullName'],
as: 'follower',
required: true // to get inner join
},
{
model: Follower,
attributes: [['followingId', 'IsFollowing']],
as: 'following',
required: false // to get left join
}
]
});