I'm building an application with express and i'm using bookshelf (mysql) js as ORM.
I have a relation many-to-many and i'm trying to store in the joining table some additional data.
My models:
const Product = Bookshelf.Model.extend({
tableName: 'products',
factors() {
return this.belongsToMany('Factor', 'product_factors', 'product_id', 'factor_id')
}
})
const Factor = Bookshelf.Model.extend({
tableName: 'factors',
products() {
return this.belongsToMany('Product')
}
})
Creating new product with relations in 'product_factors' table.
/** Create product */
router.route('/').post((req, res) => {
new Product({name: req.body.name})
.save()
.then(product => {
// attaching relation
product.factors().attach([3, 5])
.then(hz => {
res.status(201).json({
message: 'Product created'
})
})
})
})
The 'product_factors' table
|ID|product_id|factor_id|quantity|
|--|----------|---------|--------|
|1 |10 |3 |NULL |
|2 |10 |5 |NULL |
How can i insert quantity at the same time with attaching relations?
The documentation has an example of how to do that in the model.belongsToMany() section. For convenience here it is:
let Doctor = bookshelf.Model.extend({
patients: function() {
return this.belongsToMany(Patient).through(Appointment);
}
});
let Appointment = bookshelf.Model.extend({
patient: function() {
return this.belongsTo(Patient);
},
doctor: function() {
return this.belongsTo(Doctor);
}
});
let Patient = bookshelf.Model.extend({
doctors: function() {
return this.belongsToMany(Doctor).through(Appointment);
}
});
As you can see in the Doctor and Patient models you must use the .belongsToMany(Model).through(AnotherModel). Then it's just a matter of using the attach(), detach(), updatePivot() and withPivot() methods as needed to handle the data.
Also, since you're defining the joining model you can also use it directly to access and modify the data that related table contains. For example:
Appointment.forge({patient_id: 2, doctor_id: 1, confirmed: true}).save()
or
Appointment.forge({id: 4}).destroy()
Related
I'm trying to figure out why this is not working as intended. Any insights would be appreciated. Here's my situation:
I have a legacy connection of two tables with a through table. Something like:
Product model
const Product = sequelize.define('Product', {
// model attributes
});
Product.associate = (models) => {
Product.belongsToMany(models.Client, { through: models.ProductOwner, as: 'Owners'});
}
Client model
const Client = sequelize.define('Client', {
// model attributes
});
Client.associate = (models) => {
Client.belongsToMany(models.Product, { through: models.ProductOwner, as: 'OwnedProducts'});
}
ProductOwner
const ProductOwner = sequelize.define('Product', {
// no attributes
});
These form the N:M association with the through table ProductOwner.
This allows me, for example, to easily add a client to a product or getting all products that an existing client owns.
product.addOwner(client);
client.getOwnedProducts();
Now, I have the need to establish another chain of ownership to products that is unrelated to client. However, since this is still ownership, I would like to use the ProductOwner through table. So I add the new model:
Company model
const Company = sequelize.define('Company', {
// model attributes
});
Company.associate = (models) => {
Company.belongsToMany(models.Product, { through: models.ProductOwner, as: 'OwnedProducts'})
}
And the new association to the Products model. I also write a migration to add the CompanyId to the ProductOwners table and verify that the new reference is built into the database.
Product model
const Product = sequelize.define('Product', {
// model attributes
});
Product.associate = (models) => {
Product.belongsToMany(models.Client, { through: models.ProductOwner, as: 'Owners'});
Product.belongsToMany(models.Company, {
through: models.ProductOwner, as: 'CompanyOwners'
});
}
Now, on my code, I should be able to write:
product.addCompanyOwner(company);
company.getOwnedProducts();
And indeed, using the product instance method to add a new company does not throw any errors. However, the CompanyId column in the ProductOwners through table is still NULL.
Logging the query generated by Sequelize I see that the references to ProductId and ClientId are there, but there is no mention of CompanyId. Looks as if it is not recognizing that a new reference exists from Sequelize's point of view. However, the instance methods do work...
Which brings me to the question of why do they work? I assume that, by working, Sequelize is indeed creating the associations. But if that is the case, then why does the value for CompanyId is not set with the query?
Even writing it explicitly does not produce the expect result of setting CompanyId...
db.ProductOwner.create({
ProductId: 1,
ClientId: 1
}) // works to set all values
db.ProductOwner.create({
ProductId: 1,
CompanyId: 1
}) // sets ProductId to 1, but CompanyId is still NULL
What am I missing?
I need to check if entry with specific ID exists in the database using Sequelize in Node.js
function isIdUnique (id) {
db.Profile.count({ where: { id: id } })
.then(count => {
if (count != 0) {
return false;
}
return true;
});
}
I call this function in an if statement but the result is always undefined
if(isIdUnique(id)){...}
I don't prefer using count to check for record existence. Suppose you have similarity for hundred in million records why to count them all if you want just to get boolean value, true if exists false if not?
findOne will get the job done at the first value when there's matching.
const isIdUnique = id =>
db.Profile.findOne({ where: { id} })
.then(token => token !== null)
.then(isUnique => isUnique);
Update: see the answer which suggests using findOne() below. I personally prefer; this answer though describes an alternative approach.
You are not returning from the isIdUnique function:
function isIdUnique (id) {
return db.Profile.count({ where: { id: id } })
.then(count => {
if (count != 0) {
return false;
}
return true;
});
}
isIdUnique(id).then(isUnique => {
if (isUnique) {
// ...
}
});
You can count and find.
Project
.findAndCountAll({
where: {
title: {
[Op.like]: 'foo%'
}
},
offset: 10,
limit: 2
})
.then(result => {
console.log(result.count);
console.log(result.rows);
});
Doc link, v5 Beta Release
I found the answer by #alecxe to be unreliable in some instances, so I tweaked the logic:
function isIdUnique (id, done) {
db.Profile.count({ where: { id: id } })
.then(count => {
return (count > 0) ? true : false
});
}
As Sequelize is designed around promises anyway, alecxe's answer probably makes most sense, but for the sake of offering an alternative, you can also pass in a callback:
function isIdUnique (id, done) {
db.Profile.count({ where: { id: id } })
.then(count => {
done(count == 0);
});
}
}
isIdUnique(id, function(isUnique) {
if (isUnique) {
// stuff
}
});
Extending #Jalal's answer, if you're very conscious about performance implications while maintaining a simple Sequelize structure and you do not need the row data, I suggest you only request one column from the database. Why waste bandwidth and time asking the database to return all columns when you won't even use them?
const isIdUnique = id =>
db.Profile.findOne({ where: { id }, attributes: ['id'] })
.then(token => token !== null)
.then(isUnique => isUnique);
The attributes field tells Sequelize to only request the id column from the database and not sending the whole row's content.
Again this may seem a bit excessive but at scale and if you have many columns that hold a lot of data, this could make a giant difference in performance.
Try the below solution. I tried it and it works well.
const isIdUnique = async (id, model) => {
return await model.count({ where: { id: id } });
};
const checkExistId = await isIdUnique(idUser, User);
console.log("checkExistId: ", checkExistId);
How can I prevent duplication of records with the same combinations of entities, here : id_product and id_customer. When I click "save relation" a relation (many-to-many) between product and customer is created and this relation has its own id, id_product and id_customer. Is there any solution to block creation of relation between product and customer if such combination already exists in MySQL database ?
public saveRelation = (relationFormValue) => {
const newRelation = {
id_product: relationFormValue.id_product ,
id_customer: relationFormValue.id_customer
};
const dialogRef = this.dialog.open(ConfirmDialogComponent, {
maxWidth: "400px",
data: new ConfirmDialogModel("Please confirm",'Are you sure to save this relation ?')
});
dialogRef.afterClosed().subscribe(dialogResult => {
if (dialogResult==true) {
this.relationService.create(newRelation)
.subscribe(
response => {
this.dialogRef.close(true);
},
error => {
this.errorService.handleError(error);
});
}
});
}
Using the Sequelize ORM I am trying to update the field level_id where this field has a foreign key to the field Level in another table called level_tbl.
select * from level_tbl;
+----------+----------+
| level_id | Level |
+----------+----------+
| 1 | Higher |
| 2 | Ordinary |
+----------+----------+
My update task looks like this, and as you can see I am trying to get a raw sql query to work as a literal with Sequelize.
//Update task
router.put("/task/:id", (req, res) => {
if (!req.body) {
res.status(400)
res.json({
error: "Bad Data....!"
})
} else {
Task.update({
Level: req.body.Level,
Level_id: [sequelize.literal("SELECT level_id FROM level_tbl WHERE Level = 'Ordinary'")],
Year: req.body.Year,
Question: req.body.Question,
Answer: req.body.Answer,
Topic: req.body.Topic,
Sub_topic: req.body.Sub_topic,
Question_type: req.body.Question_type,
Marks: req.body.Marks,
Question_number: req.body.Question_number,
Part: req.body.Part,
Sub_part: req.body.Sub_part
}, {
where: {
id: req.params.id
}
})
.then(() => {
res.send("Task Updated")
})
.error(err => res.send(err))
}
})
What would be the correct syntax for this line?
Level_id: [sequelize.literal("SELECT level_id FROM level_tbl WHERE Level = 'Ordinary'")],
The issue is that I already have imported a model and have access to the global Sequelize instance. Therefore example in the documentation don't apply this way, i.e.,
order: sequelize.literal('max(age) DESC')
From https://sequelize.org/master/manual/querying.html
and also,
https://github.com/sequelize/sequelize/issues/9410#issuecomment-387141567
My Task.js where the model is defined is as follows,
const Sequelize = require("sequelize")
const db = require("../database/db.js")
module.exports = db.sequelize.define(
"physics_tbls", {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
Level: {
type: Sequelize.STRING
},
Level_id: {
type: Sequelize.INTEGER
},
Year: {
type: Sequelize.INTEGER
},
.........
}, {
timestamps: false
}
)
I am using a MEVN stack -> MySQL, Express.js, Vue.js and Node.js
Any help would be greatly appreciated,
Thanks,
I needed to require Sequelize again in tasks.js, the file the defines the express routes. It wasn't enough just to require Task.js although Task.js does itself require sequelize.
const Sequelize = require('sequelize')
var express = require("express")
var router = express.Router()
const Task = require("../model/Task")
Also brackets needed around the query and inside the double quotes,
Level_id: Sequelize.literal("(SELECT level_id FROM level_tbl WHERE Level = 'Higher')"),
i'm using sequelize 6.3 and raw query on where is no longer supported, i'm using this syntax :
where: sequelize.where(sequelize.col("table.column"), "=", "yourvalue")
and it worked
I'm newbie with Sails/WaterLine ORM
I'm following http://sailsjs.org/documentation/concepts/models-and-orm/associations/through-associations
One question.
How way to insert data into a join table ?
For example: User m - m Pet
User model
module.exports = {
attributes: {
name: {
type: 'string'
},
pets:{
collection: 'pet',
via: 'owner',
through: 'petuser'
}
}
Pet model
module.exports = {
attributes: {
name: {
type: 'string'
},
color: {
type: 'string'
},
owners:{
collection: 'user',
via: 'pet',
through: 'petuser'
}
}
PetUser model (join table)
module.exports = {
attributes: {
owner:{
model:'user'
},
pet: {
model: 'pet'
}
}
}
Pet data is available (some record with ID1, ID2, ID3...)
I want to add new one user with some pets
PetUser ( id , id_of_user, id_of_pet)
1, U1, P1
2, U1, P2
{
"name" : "John",
"pets" : [2,3]
}
UserController
module.exports = {
addUserWithPets: function(req, res) {
User.create(req.body).exec(function(err, user) {
if(err){
throw err;
}else {
/*pets.forEach(function(pet, index){
user.pets.add(pet);
})
user.save(function(err) {});*/
user.pets.add(data);
user.save(function(err) {});
}
return res.ok({
data: user
});
})
}
};
Thanks!
I think this hasn't been implemented yet in sails.
Refer to this question: through associations in sails.js on SO.
Here is what waterline docs say:
Many-to-Many through associations behave the same way as many-to-many associations with the exception of the join table being automatically created for you. This allows you to attach additional attributes onto the relationship inside of the join table.
Coming Soon