How to prevent inserting redundant combinations in Sequelize Node.js? - sql

I have a table for the English words: WORDS, and another for antonyms: ANTONYMS, with each word having a unique integer id in the WORDS table. The ANTONYMS table has only 2 columns: ID_1 and ID_2, both referencing ids from WORDS.
const WORDS = database.define('WORDS', {
id: {
type: Sequelise.INTEGER,
primaryKey: true,
autoIncrement: true
},
word: {
type: Sequelise.STRING,
unique: true,
validate: {
is: ['^[a-z ]{2,30}$']
},
allowNull: false
},
definition: {
type: Sequelise.TEXT,
allowNull: true,
validate: {
is: ['^[a-z0-9 .,;]{10,500}$|^$', 'i']
}
}
})
const ANTONYMS = database.define('ANTONYMS', {
id_1: {
type: Sequelise.INTEGER,
primaryKey: true,
references: {
model: WORDS,
key: 'id'
}
},
id_2: {
type: Sequelise.INTEGER,
unique: true,
references: {
model: WORDS,
key: 'id'
}
}
})
Now, suppose I have two words as antonyms, one with id=1 and the other with id=2 in the WORDS table, and the row (1, 2) has already been inserted in ANTONYMS. How to set a constraint to prevent the user from inserting the redundant value (2, 1)?

You can create a unique index in Postgres:
create unique index on antonyms (least(id_1, id_2), greatest(id_1, id_2));

Related

Sequelize - composite key made up of foreign key in another table

I'm trying to define a model with a composite key made up of the created_by and user_id fields, where the user_id is also a foreign key in the user table.
I'm following the example for composite keys from the Sequelize docs, where you have to set primaryKey to true on each of the fields which make up the composite key.
However, this isn't working - the result is that the created_by becomes the PK and user_id is a FK for the user table. When I try to insert a new record with the same created_by value, but a different user_id this doesn't work.
class my_model extends Model = (sequelize, Sequelize) => {
my_model.init({
'some_val': { type: Sequelize.DOUBLE, defaultValue: 0},
'some_val2': { type: Sequelize.DOUBLE, defaultValue: 0},
'created_by': { type: Sequelize.DATEONLY, allowNull: false, primaryKey: true},
'user_id': { type: Sequelize.INTEGER, allowNull: false, primaryKey: true, references: {model: 'user', key: 'id'}},
},
{
'indexes': [{unique: true, fields: ['user_id', 'created_by']}]
}
);
};

define belongsToMany relation with self referencing table in sequelize

i have "User" table with phoneNumber as a table column.
I want to have association so that each User can add many contacts... basically contacts will be other User.
Usually in many to many relation we have two tables and a joining table.
But can i have one table, that is User table and a joining table referencing to User itself ??
my User table:
const User = sequelize.define(
"users",
phone_number: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
} );
can i have following "UserContact" joining table without "Contact table" with this association
User.associate = (models) => {
models.User.belongsToMany(models.Contact, {through: 'UserContact' })
};
my UserContact table:
const UserContact = sequelize.define(
"user_contacts",
{
user_id: {
type: DataTypes.INTEGER,
allowNull: false,
},
contact_id: {
type: DataTypes.INTEGER,
allowNull: false,
},
}
);
Is there any other way like referencing contacts through some field in User table itself?? again one user should have one or many contacts.
You can indicate User model in a association and indicate fields for both sides:
User.associate = (models) => {
models.User.belongsToMany(models.User, {through: 'UserContact' }, foreignKey: 'user_id', otherKey: 'contact_id')
};

Sequelize define association on Array

I have two models in Sequelize as below:
export const User = db.define('user', {
id: {
type: Sequelize.UUID,
primaryKey: true,
},
});
export const Class = db.define('class', {
id: {
type: Sequelize.UUID,
primaryKey: true,
},
students: Sequelize.ARRAY({
type: Sequelize.UUID,
references: { model: 'users', key: 'id' },
})
});
How can I define an association between my Class model and the user model?
I have tried the below but it gives me an error.
Class.hasMany(User, { foreignKey: 'students' });
User.belongsTo(Class);
DatabaseError [SequelizeDatabaseError]: column "class_id" does not exist
I think you are missing the syntax
students: Sequelize.ARRAY({
type: Sequelize.UUID,
references: { model: 'users', key: 'id' }, // this has no effect
})
Should be
students: {
type: DataTypes.ARRAY({ type: DataTypes.UUID }),
references: { model: 'users', key: 'id' }
}
This won't work either, because the data type of students (ARRAY) and id (UUID) of User does not match.
Also, with these associations, you are adding two columns on User referencing id of Class but you only need one
Class.hasMany(User, { foreignKey: 'students' }); //will add students attribute to User
User.belongsTo(Class); //will add classId attribute to User
if you want to name the foreign key column passe the same name to both associations, by default Sequelize will add classId, however if you configured underscored: true on the models it will be class_id
Here is a working solution
const User = sequelize.define('user', {
id: { type: DataTypes.UUID, primaryKey: true },
});
const Class = sequelize.define('class', {
id: { type: DataTypes.UUID, primaryKey: true },
students: DataTypes.ARRAY({ type: DataTypes.UUID }),
});
Class.hasMany(User, { foreignKey: 'class_id' });
User.belongsTo(Class, { foreignKey: 'class_id' });

How to generate query on join table using Sequelize?

Let us say there are two tables namely User and User Role.
The relationship between user and user role is one to many.
Sequelize model for the user is as following -
const user = sequelize.define(
'user', {
id: {
type: DataTypes.BIGINT,
allowNull: false,
primaryKey: true,
autoIncrement: true,
field: 'id'
},
userName: {
type: DataTypes.STRING(200),
allowNull: false,
field: 'username'
},
password: {
type: DataTypes.STRING(200),
allowNull: false,
field: 'password'
}
}, {
tableName: 'user'
}
);
Sequelize model for user role is as follwing -
const userRole = sequelize.define(
'userRole', {
id: {
type: DataTypes.BIGINT,
allowNull: false,
primaryKey: true,
autoIncrement: true,
field: 'id'
},
userId: {
type: DataTypes.BIGINT,
allowNull: false,
primaryKey: true,
autoIncrement: true,
field: 'user_id'
},
password: {
type: DataTypes.STRING(200),
allowNull: false,
field: 'password'
}
}, {
tableName: 'userRole'
}
);
Sequelize association is defined as follows -
user.hasMany(models.userRole, { foreignKey: 'user_id', as: 'roles' });
userRole.belongsTo(models.user, { foreignKey: 'user_id', as: 'user' });
I want to generate the following query using
Sequelize -
SELECT *
FROM USER
INNER JOIN (SELECT user_role.user_id,
role
FROM user_role
INNER JOIN USER tu
ON tu.id = user_role.user_id
GROUP BY user_id
ORDER BY role) AS roles
ON USER.id = roles.user_id;
I am developing an API which will be consumed by the front end grid for showing user info. There is search functionality on role attribute of user role table. If any of role of a specific user is matched then I expect a user record with all the roles which are associated with that user.
To get all roles that are associated with the user even if one of them matches with a query you need to define another association so that you can include role table twice in sequelize statement.
User.hasMany(models.UserRole, { foreignKey: 'user_id', as: 'roles2' });
Sequelize statement -
const userInfo = await User.findAndCountAll({
include: [
{
model: UserRole,
attributes: ['id', 'role'],
as: 'roles',
where: { [Op.or]: [{ role: { [Op.like]: '%MANAGER%' } },
required: true
},
{
model: UserRole,
attributes: ['id', 'role'],
as: 'roles2',
required: true
}
],
where: whereStatement,
});
With the help of first include (join), you can filter user records based on user role and with the help of the second include(join), you can get all the roles of a user whose one of the roles is matched.
You have to use include (regarding the doc : https://sequelize.org/master/manual/models-usage.html#-code-findandcountall--code----search-for-multiple-elements-in-the-database--returns-both-data-and-total-count)
Exemple :
Models.User.findAll({
where :{
id: userId
},
group: ['roles.user_id'],
order: [['roles.role', 'ASC']] //or DESC, as you want
include: {
model: Models.UserRole,
as: 'roles',
attributes: ['user_id', 'role'],
required: true
},
})
Hope it helps you

How to use an alias inside an include where condition using Sequelize

A table contains 2 user id cols, userOneId and userTwoId. My user id could be in either col. I'd like to get all rows where my userId is in either col, and also include the associated object of the other user.
Here's the latest attempt. I query where my id is in userOneId or userTwoId, make an alias for the other user id, otherUserId, with a case/when. Then use alias otherUserId in an include, to return the other user associated object.
where: {
[Op.or]: [{
userOneId: userId,
}, {
userTwoId: userId,
}]
},
attributes: [
'id',
[sequelize.literal("CASE WHEN \"userOneId\" = " + userId + " THEN \"userTwoId\" ELSE \"userOneId\" END"), 'otherUserId']
],
include: {
where: {
id: '$otherUserId$'
},
model: sequelize.models.User,
as: 'otherUser',
attributes: [ 'id', 'name', ...
Model associations are:
MyModel.belongsTo(models.User, {
as: 'userOne',
foreignKey: 'userOneId'
})
MyModel.belongsTo(models.User, {
as: 'userTwo',
foreignKey: 'userTwoId'
})
User.hasMany(models.MyModel, {
as: 'userOne',
foreignKey: {
name: 'userOneId',
allowNull: false
}
})
User.hasMany(models.MyModel, {
as: 'userTwo',
foreignKey: {
name: 'userTwoId',
allowNull: false
}
})
However, it errors with
User is associated to MyModel multiple times. To identify the correct association, you must use the 'as' keyword to specify the alias of the association you want to include.
Any pointers on if or how to get this to work?