Compound/Composite key association with Sequelize - database

Does anyone know how I can use compound keys to relate to a table using Sequelize and their built in relationships?
I have tried useing primaryKey: true and unique: with no avail.

Here is the example , how you can define composite key in sequlize
sequelize.define('item_likes',{
item_id:{
type: db.Sequelize.INTEGER,
allowNull: false,
unique:"liked_user" // this will create composite key
},
user_id:{
type: db.Sequelize.INTEGER,
allowNull: false,
unique:"liked_user" // this will create composite key
},
anonymously:{
type : db.Sequelize.BOOLEAN,
defaultValue : false,
comment : "( True : user wants to be anonymous )"
}
}
All you need to do is use unique:"liked_user" , where liked_user is name of key , just for identification , and that should be same for the fields you want to make composite.
Association :
ItemLikes.belongsTo(Item,{foreignKey: 'item_id'});
Item.hasMany(ItemLikes,{foreignKey:'item_id'});
ItemLikes.belongsTo(User,{ foreignKey : 'user_id'});
User.hasMany(ItemLikes, { foreignKey: 'user_id'});

Related

How to save entity with a foreign key referencing itself (recursive foreign key) in TypeORM

I'm new to TypeORM and I'm stuck in a chicken/egg scenario. I have the following entity:
#Index("institutes_pkey", ["idInstitute"], { unique: true })
#Entity("institutes", { schema: "public" })
export class Institutes {
#PrimaryGeneratedColumn({ type: "bigint", name: "id_institute" })
id_institute: string;
#Column("text", { name: "name" })
name: string;
#Column("text", { name: "description" })
description: string;
#ManyToOne(() => Institutes, (institutes) => institutes.institutes)
#JoinColumn([{ name: "id_grupo", referencedColumnName: "idInstitute" }])
id_group: Institutes;
#OneToMany(() => Institutes, (institutes) => institutes.id_group)
institutes: Institutes[];
}
child_institutes can be grouped under the umbrella of a bigger father_institute.
In that case the the id groups would look like this:
father_institute.id_group = father_institute.id_institute
child_institutes.id_group = father_institute.id_institute.
If a institute won't be part of any group, it's id_group equals it's id_institute (same as father case). And here lies my issue, when I need to save a father_institute, it's FK references the very same object I'm trying to save. What can I do in that case?
I imagined a workaround, create a sequence in PSQL and set id_institute with that sequence then set the default value of id_group to the current value of this sequence. But is there any better solution for this?

Work around Sequelize’s unique constraints in belongsToMany associations

I'm using Sequelize in my project. These are the two models:
const User = db.define('user', {
name: Sequelize.STRING,
password: Sequelize.STRING
})
const Product = db.define('product', {
name: Sequelize.STRING,
price: Sequelize.INTEGER
})
Now users can purchase products and I have associations setup like below:
Product.belongsToMany(User, {through: 'UserProducts'})
User.belongsToMany(Product, {through: 'UserProducts'})
I also have this UserProducts table with an additional column.
const UserProducts = db.define('UserProducts', {
status: Sequelize.STRING
})
Sequelize creates a composite key with combination of userId and productId and will only allow one record with a combination of userId and productId. So, for example, userId 2 and productId 14.
This is a problem for me because sometimes people want to purchase multiple times. I need one of the following scenarios to work:
Don't use the composite key and instead have a completely new auto-increment column used as key in UserProducts.
Instead of making key with userId and productId alone, allow me to add one more column into the key such as the status so that unique key is a combination of the three.
I do want to use the associations as they provide many powerful methods, but want to alter the unique key to work in such a way that I can add multiple rows with the same combination of user id and product id.
And since my models/database is already running, I will need to make use of migrations to make this change.
Any help around this is highly appreciated.
If anyone else is having problems in v5 of Sequelize, it is not enough to specify a primary key on the 'through' model.
You have to explicitly set the unique property on the through model.
User.belongsToMany(Product, { through: { model: UserProducts, unique: false } });
Product.belongsToMany(User, { through: { model: UserProducts, unique: false } });
Belongs-To-Many creates a unique key when the primary key is not present on through model.
Since you also have additional property in your UserProducts table, you need to define the model for UserProducts and then tell the sequelize to use that model instead of creating its own
class User extends Model {}
User.init({
name: Sequelize.STRING,
password: Sequelize.STRING
}, { sequelize })
class Product extends Model {}
ProjProductect.init({
name: Sequelize.STRING,
price: Sequelize.INTEGER
}, { sequelize })
class UserProducts extends Model {}
UserProducts.init({
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
status: DataTypes.STRING
}, { sequelize })
User.belongsToMany(Project, { through: UserProducts })
Product.belongsToMany(User, { through: UserProducts })
refer: Sequelize v4 belongsToMany
UPDATE
since you are using v3 and already have a model created for your UserProducts table use following snippet
UserProducts = db.define('UserProducts', {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
status: DataTypes.STRING
})
Since setting explicitly the unique property on the through model is not working in v6, the only solution i found is to define the 3 parts of the association this way :
User.hasMany(UserProducts);
UserProducts.belongsTo(User);
Product.hasMany(UserProducts);
UserProducts.belongsTo(Product);
You can then create your models and associations :
const user = await User.create(user_data);
const product = await Product.create(product_data);
const up = await UserProduct.create(up_data);
await up.setUser(user);
await up.setProduct(product);
If anyone has a better solution, I would be happy to know it

omitNull not working on primaryKey column in Sequelize

I have a SQL server database with a table which auto inserts a guid in each row when a record is added. This column is the primary key for the table and never needs a value to be supplied to it as it's automatic.
The trouble is that Sequelize is sending NULL for this column every time I do a .create({emailAddress:"test#test.com"}) call which is causing an error in the database (because nulls aren't allowed, obviously).
I've tried adding omitNull at the top level and at the "call" level and neither of them work , unless I remove the primary key and then it doesn't send a NULL. So it seems that Sequelize thinks that if something is the primary key then it must send a value, not understanding that the SQL SERVER database is going to handle insertion of that value.
Anybody know a workaround?
// Model
module.exports = function (sequelize, DataTypes) {
const Player = sequelize.define('Player', {
playerId: {
primaryKey: true,
type: DataTypes.STRING
},
emailAddress: {
type: DataTypes.STRING
}
}, {
timestamps: false
});
return Player;
};
// Create a row
let newPlayer = {
emailAddress:'test#test.com'
}
Player.create(newPlayer, {omitNull:true}).then(function(player){
console.log("player", player)
}).catch(function(error){
console.log("error", error)
})
Adding defaultValue and allowNull should do the job
playerId: {
primaryKey: true,
defaultValue: '',
allowNull: false,
}

Mongoose - Remove several objects from an array (not exact match)

I have a collection Playlist that contains an array of items
{
userId: {
type : String,
required : true,
index : true,
unique : true
},
items: [
{
id: { // do not mix up with _id, which is the autogenerated id of the pair {id,type}. ID is itemId
type : Schema.Types.ObjectId
},
type: {
type : String
}
}
]
}
Mongo automatically adds the _id field to the items when I push a pair {id,type} to items (but I don't care about it).
Now I would like to remove several "pairs" at once from the items array.
I have tried using $pullAll but it requires an exact match, and I do not know the _id, so it does not remove anything from items
playlistModel.update({userId:userId},{$pullAll:{items:[{id:"123",type:"video"},{id:"456",type:"video"}]}},null,function(err){
I have tried using $pull with different variants, but it removed ALL objects from items
playlistModel.update({userId:userId},{$pull:{items:{"items.id":{$in:["123","456"]}}}},null,function(err){
playlistModel.update({userId:userId},{$pull:{items:{$in:[{id:"123",type:"video"},{id:"456",type:"video"}]}}},null,function(err){
Am I missing something or am I asking something that isn't implemented?
If the latter, is there a way I can go around that _id issue?
OK I found a way that works using $pull:
playlistModel.update({userId:userId},{$pull:{items:{id:{$in:["123","456"]}}}},null,function(err){
It doesn't take the type into account but I can't see any issue with that since the id is unique across all types anyway.
Although I will wait a bit to see if someone has a better solution to offer
EDIT
With Veeram's help I got to this other solution, which IMO is more elegant because I don't have _ids that I don't need in the database, and the $pullAll option seems more correct here
var playlistItemSchema = mongoose.Schema({
id: { // do not mix up with autogenerated _id. id is itemId
type : Schema.Types.ObjectId
},
type: {
type : String
}
},{ _id : false });
var schema = new Schema({
userId: {
type : String,
required : true,
index : true,
unique : true
},
items: [playlistItemSchema]
});
playlistModel.update({userId:userId},{$pullAll:{items:[{id:"123",type:"video"},{id:"456",type:"video"}]}},null,function(err){
tips:
you can use _id field to handle your playlistModel data.
mongoose api : new mongoose.Types.ObjectId to generate an Object_id
let _id=new mongoose.Types.ObjectId;
playlistModel.updateMany({_id:_id},{ $set: { name: 'bob' }}).exec(data=>{console.log('exec OK')});

Foreign key constraint on SailsJS

This is my College model
module.exports = {
attributes: {
name:{
type:'string',
required:true
},
location:{
type:'string',
required:true
},
faculties:{
collection:'faculty',
via:'college'
}
}
};
This is my Faculty model
module.exports = {
attributes: {
name:{
type:'string',
required:true
},
description:{
type:'string'
},
college:{
model:'college',
required:true
}
,
years:{
collection:'year',
via:'faculty'
}
}
};
My problem is that I can add new Faculty with any value in college attribute. If I don't have college with 3000 id, I can still add it but college attribute won't show up when I list all faculties. How can I prevent it from adding a faculty with invalid college id?
Currently waterline does not create foreign key constraints in the manner you describe. It only creates the associated field.
You can use a different library instead of Waterline such as Sequelize.js here is a link about how to go about doing that
https://groups.google.com/forum/#!topic/sailsjs/ALMxbKfnCIo
If your are using a SQL database you can manally create the foreign key constraint yourself.
Or you can validate the value of college before being set in your faculty model by checking in beforeValidate() or afterValidate() on your faculty model.

Resources