How can I update a through-NM-attribute in sequelize? - mysql

I've read this question and it didnt' help me, so I'm asking my own:
Let's suppose I have 2 tables joined by a NM through table using Sequelize and MariaDB:
User <-- UserItem --> Item
A single User can have many Items, and a single Item can belong to many Users. But I need a custom through attribute to store the quantity of the Item, let's call it Apples. So, according to the docs, this will be the definition:
var UserItem = Sequelize.define('UserItem', {
quantity: DataTypes.INTEGER
},
timestamps: false
});
models.Item.belongsToMany(models.User, {through: 'UserItem'});
models.User.belongsToMany(models.Item, {through: 'UserItem'});
And then I add a new relationship with the through attribute like this:
User.addItem(item, { quantity: 0 });
This works as expected. But, what if I need to update the quantity of an Item? I could do the following:
User.addItem(item, { quantity: 20 });
And the quantity of my Item will be updated to 20 in case of existing, and inserted otherwise. I don't want this. I want something like that:
User.addItem(item, { quantity: quantity + 1 });
But due to the impossibility to make queries to the join tables, I am unable to get the particular NM row for updating using the previous value.
How can I achieve this? Thanks in advance.

You still have the join table DAO as specified in the docs.
In this case though you need to know if the item is new association or not.
So it will look something like.
User.hasItem( item )
.then( exists => {
if ( !exists ) {
return User.addItem( item, { quantity : 20 } )
} else {
item.UserItem.quantity += 1;
return item.UserItem.save();
}
} )
You can read more about hasAssociation in the docs as well.

Related

Do I need to use Prisma's connect & disconnect API? Or is it not safe to just update my Relations via IDs as strings?

I am using prisma + mysql (on planetscale). When I link two items that are in different tables, I normally use connect or disconnect:
const getUser = await prisma.user.update({
where: {
id: 9
},
data: {
posts: {
| connect: {
| id: 11
| },
create: {
title: "My new post title"
}
}
}
})
I am wondering whether that's necessary or why that's necessary?
I also noticed that I can just update records in my database by updating the id (as a plain string), and it will still work. e.g.:
// example for updating a one-to-many relationship:
const getUser = await prisma.user.update({
where: {
id: 9
},
data: {
postId: "123192312i39123123"
}
}
})
... or if it's an explicit many-to-many relation, I can just edit the row in the relation-table & update the id.
Is this a bad way of doing things? Am I going to break something later down the line in doing it this way?
Your cloud provider is not relevant in the context of the question. It will not affect how your framework(prisma) behaves in updates.
I am wondering whether that's necessary or why that's necessary?
You have a user with a one to many relation: user => n posts.
You have an existing post in the db, and you want to add that post to the posts collection of a user.
That posts relation can be either explicit or implicit. The connect clause handles the addition of relation:
{
posts: {
connect: { id: 11 }
}
}
Without using the connect you'd have to create a new post:
{
posts: {
create: {
title: "My new post title"
}
}
}
update records in my database by updating the id (as a plain string)
Not sure what you mean here, mind sharing the schema?
or if it's an explicit many-to-many relation, I can just edit the row in the relation-table & update the id
If it's explicit many-to-many then it's OK to manually edit the id fields. As long as the ids are found and the relation makes sense, there's no problem with manual updates.

Is it possible to order intermediate relation table using sequelize?

I have the following scenario, my application has two entities: box and items with N to N relationship. I am using sequelize with MySQL.
I am using pseudocode to represent the tables:
Box {
id: Integer primary key
name: String
}
Item {
id: Integer primary key
name: String
}
I have set up the schemas with relations hasMany in both directions using the following through relation:
Box.hasMany(Item, { through: Box_Item });
Item.hasMany(Box, { through: Box_Item });
Box_Item {
id_box: Integer,
id_item: Integer,
item_order: Integer
}
With primary_key(id_box, id_item).
I tested it and I can call myBox.getItems() on my instance object myBox and easily get all the items it has.
I can make calls as
BoxModel.findOne({
where: { id: 1 },
include: [{ model: ItemModel }]
});
And it automatically understands there is a relation between the models through Box_Item and get everything correctly, except that I'm not getting the results sorted by item_order field. This field is a number from 1 to N that represents the item order inside that box.
I tried
BoxModel.findOne({
where: { id: 1 },
include: [
{
model: ItemModel,
order: 'item_order'
}
]
});
But it seems sequelizejs does not support order inside include yet (checked on their github repo).
I tried to force
BoxModel.findOne({
where: { id: 1 },
order: '`box_model`.`item_order`'
include: [ { model: ItemModel } ]
})
looking through the query sequelize creates but it just put the ORDER BY in two different places (inside INNER JOIN and at the end of the query, don't know why...) and I got an error.
So I searched for this on stackoverflow (1), found a few questions but I don't get a good way for doing that using the ORM.
How could I get the items sorted by item_order field when asking for specific box items?
After a few days trying to get it done I found an answer on stackoverflow that helped me.
After creating the relationships between Box and Item I can easily call on an instance:
myBox.getItems({
order: '`box_model`.`item_order`'
});
And then I get the result I'm expecting. But I had to look through the query sequelize is creating based on the models and get the correct field based on their renaming rules.
If you want you can pass the as parameter and rename your tables.

Sequelize Query Group by day objects found by parent property

So, I'm using sequelize with a mysql instance and I have this hierarchy : a task has n images and also n metadata key value pairs.
I want to get all images based on userId column of task, and afterwards get them grouped by 'createdAt' column taking into consideration the day, since a normal groupby will be pointless ( no objects share the same datetime ). I did lots of test to try to group, and I ended up using this query, which gives bad results ( I have like 11 images for a task, and it retrieves 4 ). Honestly, i don't know what I'm doing wrong. Any of you have any idea?
This is the code snippet:
var userId = security.utils.getKeycloakSubject(req);
var where = {
userId: userId
};
db.image.findAll({
include: [{
model: db.task,
include: [{
model: db.metadata,
as: 'metadata'
}],
where: where
}],
group: [db.Sequelize.fn('DAY', db.Sequelize.col('image.createdAt'))]
}).then(function (images) {
return res.json(images);
}, function (error) {
return res.status(500).json(error);
})
I saw your question and also found this: Sequelize grouping by date, disregarding hours/minutes/seconds
It is a question about group the DAY(createdAt), looks similar with yours.
And my solution of GROUP BY DAY() is look like:
item.findAll({
attributes:
[[sequelize.fn('DATE_FORMAT', sequelize.col('TimeStamp'), '%H'), 'dates']],
group: [sequelize.fn('DAY', sequelize.col('TimeStamp'))]
}).
then(function(result){console.log(result)
}).
catch(function(error){}).catch(function(error) {
console.log(error);
});
So the raw SQL likes: SELECT DATE_FORMAT('TimeStamp', '%H') as 'dates' FROM tableName GROUP BY DAY('TimeStamp');
Hope it helps you, or you can show us the SQL you want to use, maybe it is easier to help you too.
Good luck.

How to count association size with waterline/sails?

Using sails 0.10.5/waterline 0.10.15:
I cannot find an answer to a simple question: how to count the elements of an association without using populate() (which would load all data).
Let take a simple many2many relation with via:
User:
attributes: {
following: {
collection: 'user',
via: 'follower',
dominant: true
},
follower: {
collection: 'user',
via: 'following'
}
Now I need the size of the collections.
Currently I try
User.findById(1).populateAll().exec(function(err, user) {
// count of followings -> user.following.length;
// count of followers-> user.follower.length;
}
which leads to loading the collections.
I'm missing a count function at collection level to avoid population/loading of data.
Is there a possibility to access the (auto generated) join tables to run a count-query directly on the join?
Something like:
User.findById(1).count({'followings'}).exec(function(err, followings) {
...}
or
UserFollowingFollow_FollowFollowing.countByUserFollowingFollowId(1).
exec(function(err, followings) {
...}
Waterline does offer the count query method and it can be used like this to solve your problem:
User.count().where({follower: followerId})
.exec(function(err, numberOfFollowings) {
//numberOfFollowings will be the integer that you need
})
followerId is the id that you are passing to User.findOne() in your example.
You can also read the Waterline documentation about this.

How to get relationship/ assosiation in sequelizejs ORM

By below reference I understood how map many to many with a relationship table
http://sequelizejs.com/docs/latest/associations#many-to-many
User = sequelize.define('User', { user_name : Sequelize.STRING})
Project = sequelize.define('Project', { project_name : Sequelize.STRING })
UserProjects = sequelize.define('UserProjects', {
status: DataTypes.STRING
})
User.hasMany(Project, { through: UserProjects })
Project.hasMany(User, { through: UserProjects })
But how to query Project 's of a User
I Tried like
User.find({where:{id:1},include,[UserProjects]})
User.find({where:{id:1},include,[Projects]})
User.find({where:{id:1},include,[UserProjects]})
User.find({where:{id:1},include,[Projects]})
But i dont get results
Sequelize created table like below
users(id,name)
projects(id,project_name)
userprojects(id,UserId,ProjectId)
I tried https://github.com/sequelize/sequelize/wiki/API-Reference-Associations#hasmanytarget-options
User.find({where:{id:1}}).success(function(user){
user.getProjects().success(function (projects) {
var p1 = projects[0] // this works fine but 2 queries required. I expect in single find. without getProjects
p1.userprojects.started // Is this project started yet?
})
})
How to get all the projects of a USER ??
You should be able to get all of the properties of the user in two different ways: using includes and getting the projects from a user instance.
Using includes the code you submitted above is almost right. This method will only make one query to the database using the JOIN operation. If you want all of the users with their corresponding projects, try:
User.findAll({include: [Project]})
You can also get the projects directly from a user instance. This will take two queries to the database. The code for this looks like
User.find(1).then(function(user) {
user.getProjects().then(function(projects) {
// do stuff with projects
});
});
Does this work for you?