Sequelize Composite Foreign Key - node.js

I have a database with the following tables:
CREATE TABLE IF NOT EXISTS `app_user` (
`user_id` INT NOT NULL,
`user_name` VARCHAR(45) NOT NULL,
PRIMARY KEY (`user_id`))
ENGINE = InnoDB;
CREATE TABLE IF NOT EXISTS `user_folder` (
`user_id` INT NOT NULL,
`document_id` INT NOT NULL,
PRIMARY KEY (`user_id`, `document_id`),
CONSTRAINT `fk_user_document_user`
FOREIGN KEY (`user_id`)
REFERENCES `zinc`.`app_user` (`user_id`)
ON DELETE CASCADE
ON UPDATE CASCADE)
ENGINE = InnoDB;
CREATE TABLE IF NOT EXISTS `folder_content` (
`user_id` INT NOT NULL,
`document_id` INT NOT NULL,
`content_id` INT NOT NULL,
PRIMARY KEY (`user_id`, `document_id`, `content_id`),
CONSTRAINT `fk_folder_content_folder`
FOREIGN KEY (`user_id` , `document_id`)
REFERENCES `zinc`.`user_folder` (`user_id` , `document_id`)
ON DELETE CASCADE
ON UPDATE CASCADE)
ENGINE = InnoDB;
I need to create a Sequelize model to represent it. The only problem I have is with the relation folder_content and user_folder because of the composite key.
How can I create this sequelize model?
This is what I have so far:
var AppUser = sequelize.define('app_user',
{userId: {type: Sequelize.INTEGER, primaryKey: true, field: 'user_id'}, ... } );
var UserFolder = sequelize.define('user_folder',
{userId: {type: Sequelize.INTEGER, primaryKey: true, field: 'user_id'},
documentId: {type: Sequelize.INTEGER, primaryKey: true, field: 'document_id'}... });
var FolderContent = sequelize.define('folder_content', {
userId: {type: Sequelize.INTEGER, primaryKey: true, field: 'user_id'},
documentId: {type: Sequelize.INTEGER, primaryKey: true, field: 'document_id'},
contentId: {type: Sequelize.INTEGER, primaryKey: true, field: 'content_id'}... });
UserFolder.hasMany(FolderContent);
FolderContent.belongsTo(UserFolder, {foreingKey: !! });// <- PROBLEM

Now Sequelize doesn't support composite foreign keys. This creates several problems.
When Sequelize creates a table, the table definition does not have a composite FK.
To solve this problem I use the afterSync hook on the model and a function
that adds a FK to the table if it does not exist. Example code.
When I use the findAll method with include such model, I use the include[].on option of the findAll method. Or if you don't use as many joins as I do, you can use scope when creating an association (see).

does sequelize still not support composite foreign keys? i would like to have a table with 2 foreign keys as the composite primary key. i would rather have it this way than have a surrogate key as the primary key with a unique constraint over the 2 foreign key fields.
thanks

Related

Sequelize: how to let the database handle primary key value?

I'm using sequelize 6.5.0. I created a simple model to do two rudimentary things: a) find records, b) create records. I'm having trouble creating records; specifically, ones with primary key. If I designate the column as primaryKey like so:
const Table = sequelize.define('table', {
id: {
type: DataTypes.UUID,
primaryKey: true
},
datum: {...}
...
and try to create a record like so:
Table.create({datum: 'abc'})
then it will try (and fail) to set the primary key with:
INSERT INTO "table" ("id","datum") VALUES ($1,$2) RETURNING ...;
which is 50% what I did not ask it to do. Now, I don't need this to happen since default value for id is already handled at the database level. So, the next natural move was to not designate id as primaryKey:
const Table = sequelize.define('table', {
id: {
type: DataTypes.UUID,
// primaryKey: true
},
datum: {...}
...
But now sequelize attempts to get smart and throws a tantrum:
Uncaught Error: A column called 'id' was added to the attributes of 'table' but not marked with 'primaryKey: true'
Q) How do I get sequelize to NOT handle primary key on create?
I think you can skip the id field in the definition altogether, and PostgreSQL will still have one

how to create parent/child associations in sequelize?

I have a Employee and their dependents model defined in sequlieze. I have an employeeId foreign key column defined in the dependent table ( see my model class below) . but when i try to execute following command
models.Employee.findOne({where: { id: Number(id) }, include: [{ model: models.Dependent }]});
i get an error -> EagerLoadingError [SequelizeEagerLoadingError]: Dependent is not associated to Employee!
isn't specifying a foreign key in the dependent model , enough?
Employee model
module.exports = function(sequelize, DataTypes) {
const Employee = sequelize.define('Employee ', {
id : {
type: DataTypes.INTEGER(11),
allowNull: false,
autoIncrement:true,
primaryKey:true
},
Name : { ... }
Dependent model
module.exports = function(sequelize, DataTypes) {
const Dependent = sequelize.define('Dependent', {
id : {
type: DataTypes.INTEGER(11),
allowNull: false,
autoIncrement:true,
primaryKey:true
},
EmployeeId : {
type: DataTypes.INTEGER(11),
allowNull: true,
references : {
model : 'Employee ',
key:'id'
}
},
Name : { ... }
isn't specifying a foreign key in the dependent model, enough?
No, it's NOT enough if you want to use Eager Loading of sequelize.
You need to create sequelize associations for these models(Employee and Dependent for your case) in pairs, see Why associations are defined in pairs?
The references option in the model only creates an FK relationship between Dependent and Employee tables in the database(relationship for db level). Does not include associations of sequelize model(relationship for application level). When to use? see Enforcing a foreign key reference without constraints
Employee has many dependents and dependent belongs to one Employee. So you need to create One-To-Many relationships.
Employee.hasMany(Dependent);
Dependent.belongsTo(Employee);
create associations like above will create FK reference for tables implicitly.
Execution results:
Executing (default): CREATE TABLE IF NOT EXISTS "employees" ("id" SERIAL , "name" VARCHAR(255), PRIMARY KEY ("id"));
Executing (default): CREATE TABLE IF NOT EXISTS "dependents" ("id" SERIAL , "name" VARCHAR(255), "EmployeeId" INTEGER REFERENCES "employees" ("id") ON DELETE SET NULL ON UPDATE CASCADE, PRIMARY KEY ("id"));

How to remove unique option using sequelize

I am using sequelize at nodejs.
When I was making, I set the unique option in a column called 'invoice'.
But since I need to remove the unique option, I have to use migration.
queryInterface.removeConstraint('my_some_table', 'my_constraint');
I saw this command, and I think it is not correct method for me.
How can I remove 'unique option' using migration at sequelize?
invoice: {
type: DataTypes.STRING(50),
unique: true, <<-- I want to remove this.
allowNull: false,
},
This is from the Sequelize documentation and allows you to change the meta data on a column:
queryInterface.changeColumn(
'nameOfAnExistingTable',
'nameOfAnExistingAttribute',
{
type: Sequelize.FLOAT,
allowNull: false,
defaultValue: 0.0
}
)

Alphanumeric ID in sequelize.js

I'm using sequelizejs with PostgreSQL as ORM in my webapp, I want to use alphanumeric ID instead numeric.How can I do that ?
Is there some way to do it through sequelize?
Or do I want to generate this id separately and then save it into db ?
Thanks for any help.
You can use a UUID as primary key, and set its default value to uuidv1 or v4
http://docs.sequelizejs.com/en/latest/api/datatypes/#uuid
id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
primaryKey: true
}

REFERENCE KEY in node-orm2

I am working on REST API based on node.js and i chose postgresql database to store data. Suppose that this database has two tables names User and Comment. Any Comment belongs to One User and when we decide to remove an User, the Comment's of him/her must be removed. So, I designed my table as follows:
CREATE TABLE User(
user_id SERIAL,
username VARCHAR(32) NOT NULL,
password VARCHAR(32) NOT NULL,
CONSTRAINT pk_user PRIMARY KEY (user_id),
CONSTRAINT uq_user UNIQUE (username)
)
CREATE TABLE Comment(
comment_id SERIAL,
user_id INTEGER NOT NULL,
content TEXT NOT NULL,
CONSTRAINT pk_cmnt PRIMARY KEY (comment_id),
CONSTRAINT fk_cmnt FOREIGN KEY (user_id) REFERENCES User(user_id)
ON UPDATE CASCADE ON DELETE CASCADE
)
But i don't run this code and use node-orm2 instead. I designed two simple models to handle this simple code:
var User = db.define('user', {
username: {
type: 'text',
size: 32, // VARCHAR(32)
required: true, // NOT NULL
unique: true // UNIQUE INDEX
},
password: {
type: 'text',
size: 32, // VARCHAR(32)
required: true // NOT NULL
}
}, {
id: 'user_id' //SERIAL
});
var Cmnt = db.define('comment', {
content: {
type: 'text',
required: true // NOT NULL
}
}, {
id: 'comment_id' //SERIAL
});
Cmnt.hasOne('user', User, {required: true}); // CREATE ASSOCIATION KEY
and synchronize database with these models :
db.sync();
Now, I want to insert new comment belongs to user which user_id doesn't exist. So, the Comment model accepts this and insert the row into comment table.
My question is, how can i do some things like REFERENCE KEY and the ON UPDATE CASCADE ON DELETE CASCADE ?
Thanks in advance :)
Try to use deferrable like this:
CREATE TABLE Comment(
comment_id SERIAL,
user_id INTEGER NOT NULL,
content TEXT NOT NULL,
CONSTRAINT pk_cmnt PRIMARY KEY (comment_id),
CONSTRAINT fk_cmnt FOREIGN KEY (user_id) REFERENCES User(user_id)
ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED
)

Resources