Is it possible in sequelize to query based on an association? - javascript

I modelled a data structure where I have a n:m relation between "Rooms" and "Users".
Now I want to delete / destroy a room.
Therefore I want to check if the deleting user is in the room.
I have the username and the roomid.
How can I accomplisch that in one Query.
Basically my question is of it possible to do something like that in a query:
Room.destroy({
where: {
//username in users
users: {$contains: { username: "some username" }}
}
})
Here users is an "Association" to my users.

Considering your users model is defined as User,
this may helps you (or at least gives you a start point):
Room.destroy({
include: [{
model: User,
through: {
where: {username: "some username"}
}
}]
});
This is described in the n:m querying section in Sequelize documentation:
With Belongs-To-Many you can query based on through relation and
select specific attributes. For example using findAll with through
User.findAll({
include: [{
model: Project,
through: {
attributes: ['createdAt', 'startedAt', 'finishedAt']
where: {completed: true}
}
}]
});

Related

Disconnecting Many-to-Many Relationships in Prisma + MySQL

I am so completely lost. I have an explicit many to many relation: Users can have multiple Lists, but lists can be owned by multiple users:
model List {
id String #id #default(cuid())
title String
users UsersOnLists[]
}
model User {
id String #id #default(cuid())
name String
lists UsersOnLists[]
}
model UsersOnLists {
id String #id #default(cuid())
order Int
user DictItem? #relation(fields: [userId], references: [id])
userId String?
list List? #relation(fields: [ListId], references: [id])
listId String?
}
Now I'd like to connect a list to a user:
prisma.list.update({
where: {
id: input.id
},
data: {
users: {
create: [{
order: 123,
user: {
connect: {
id: "abcd-123",
}
}
}],
}
}
});
This works.
However, I don't know how to go about disconnecting many-to-many relations in prisma? Say I want to disconnect the user again from the list? How would I do this?
prisma.list.update({
where: {
id: input.id
},
data: {
users: {
disconnect: [{
user: {
disconnect: {
id: "abcd-123",
}
}
}],
}
}
});
This doesn't work.
I also can't find much in the prisma docs about disconnecting. Any ideas?
I guess I could jus delete the row from the Relations-Table, but this doesn't feel as clean and I guess I would still have the old ids in the user & list tables? I would prefer using disconnect, if this is the recommended method for that.
Are you getting a specific error? If you are using a code editor/IDE with TypeScript hinting, it should be giving you a specific error(s) about what's going on. If not that, then the command line should be giving you errors when you attempt to run an operation.
Docs: https://www.prisma.io/docs/concepts/components/prisma-client/relation-queries#disconnect-a-related-record
The "disconnect" operation cannot disconnect deeply-nested relations. It only disconnects documents directly connected to the model in question. In your situation, you can only disconnect a UserOnList from a List, but you cannot also disconnect User from UserOnList in the same operation.
prisma.list.update({
where: {
id: input.id
},
data: {
users: {
disconnect: [{
id: "ID_OF_UsersInList_MODEL_HERE"
}],
}
}
});
Also - you don't need the UsersInList table. Prisma can manage the "join" table under the hood for you if you don't need any extra information or data on that model. Check out the docs here if you want Prisma to manage this table on its own: https://www.prisma.io/docs/concepts/components/prisma-schema/relations/many-to-many-relations

Exclude certain attributes from object using the populate from mongodb using aggregate

I want to exclude for example email and address using populate() function from mongodb, just get the name from it:
Example:
const results = await Seller.aggregate(aggregatePipeline).exec();
const sellers = await Seller.populate(results, { path: "user" });
When populating the user instead of having:
...
user: {
email: "hgjh#gmail.com",
address:{},
name: "name"
}
I want to only have (exclude certain data from the path):
...
user: {
name: "name"
}
You can do either,
const sellers = await Seller.populate(results, { path: "user", select: '-
email -address' });
or
const sellers = await Seller.populate(results, { path: "user", select:
'name' });
As i understand mongoose documentation https://mongoosejs.com/docs/populate.html, populate as $lookup is use to resolve a relation with other collection.
MongoDB has the join-like $lookup aggregation operator in versions >= 3.2. Mongoose has a more powerful alternative called populate(), which lets you reference documents in other collections.
In your case, you don't need to resolve a field with an other collection. You already have the final data you target . You could use $project at the end of your pipeline aggregation to keep only name field, like :
{ $project: { name:1 } }
Let me know if i helped you.
Edit :
I read too fast, if you have this data res after the populate and not after the aggreg, you may select your final field, like is said
here https://stackoverflow.com/a/72481338/16205278
user: {
email: "hgjh#gmail.com",
address:{},
name: "name"
}

Sequelize self-referential many-to-many; how to specify what I'm joining on

I have a Sequelize structure that associates my User table to itself through a Follow table, with follower and following. I'm 99% certain that I have all of the necessary pieces for this to be working the way I expect it to; I've the aliased
User.belongsToMany(models.User, { through: models.Follow, as: 'Followers', foreignKey: 'follower'});
User.belongsToMany(models.User, { through: models.Follow, as: 'Following', foreignKey: 'following'});
as well as
User.hasMany(models.Follow, { foreignKey: 'following' });
User.hasMany(models.Follow, { foreignKey: 'follower' });
and
Follow.belongsTo(models.User, { foreignKey: 'following', as: 'Following' });
Follow.belongsTo(models.User, { foreignKey: 'follower', as: 'Follower' });
And I'm able to call
User.findByPk(id, {
include: {
model: Follow
}
});
which returns a single user and an array of Follow entries.
The problem I'm having is, Sequelize seems to be defaulting to creating the Follow query as where: { followING: User.id } rather than where: followER, such that if I have 1 user FOLLOWING themselves and 2 other people, but only FOLLOWED BY themselves, it only returns 1 result from my Follow table. As I was cycling through the primary keys for the handful of users in my seed, only the following value in my results change, such that I'm only ever returning a users' followers, not the other users that user is following.
Is there a way to specify in an include which specific column I'm trying to join on, when multiple columns match the Sequelize object to which I'm joining?
I understand that worst case scenario I can always skip the User step and go straight to
Follow.findAll({
where: {
follower: id
}
})
But that restricts my immediate access to the User object and I'd have to write an additional query, which seems cumbersome considering a self-associated many-to-many capability exists.
If you wish to get a user with followers or with following users you don't need to indicate Follow table. All you need is to indicate an alias of a required association:
User.findByPk(id, {
include: {
model: User,
as: 'Followers'
}
});
User.findByPk(id, {
include: {
model: User,
as: 'Following'
}
});
In case you need to include Follow directly you need to add an associations like this:
User.hasMany(models.Follow, { as: 'FollowerLinks', foreignKey: 'follower'});
User.hasMany(models.Follow, { as: 'FollowingLinks', foreignKey: 'following'});
And you also should indicate an alias so that way Sequelize will know what association to use:
User.findByPk(id, {
include: {
model: Follow,
as: 'FollowerLinks'
}
});

How to disambiguate between multiple associations between the same models in Sequelize

I have three models ā€” Book, User and Institution ā€” which are associated to one another as follows:
Books are associated to Institutions via a Book_Institution join table (many to many relationship)
Book.belongsToMany(models.Institution, { through: 'Book_Institution' })
and
Institution.belongsToMany(models.Book, { through: 'Book_Institution' })
Users can be associated to Institutions in two ways: as reader or author. This is done via two join tables: Author_Institution and Reader_Institution:
Institution.belongsToMany(models.User, { through: 'Author_Institution' })
Institution.belongsToMany(models.User, { through: 'Reader_Institution' })
and
User.belongsToMany(models.Institution, { through: 'Author_Institution' })
User.belongsToMany(models.Institution, { through: 'Reader_Institution' })
(Each time leaving out foreignKey for brevity.)
I want to query the Book model to find all books that belong to an author. Sequelize provides the include option to easily join two associated tables. The problem Iā€™m stuggling with is that using include as shown below defaults to the Reader_Institution association. How can I specify which association should be used?
getBooks: (obj, args, context) => {
const { user } = context
return Book.findAll({
attributes: ['id', 'path'],
include: [{
include: [{
attributes: ['id'],
model: User,
where: { id: user }
}],
model: Institution,
required: true // inner join
}]
})
}
Thanks in advance for your help.
I use as which allows you to reference the relationship through that alias.
Institution.belongsToMany(models.User, {
through: 'Author_Institution', // many-to-many relationship table name
as: 'AuthorInstitution' // alias
})
With your models set up this way, you can use as to to specify which relationship you want to include when querying.
getBooks: (obj, args, context) => {
const { user } = context
return Book.findAll({
attributes: ['id', 'path'],
include: [{
include: [{
attributes: ['id'],
model: User,
where: { id: user },
as: 'AuthorInstitution'
}],
model: Institution,
required: true // inner join
}]
})
}
Also, with this methodology, it allows you you to reference the relationship data via the as, so you can do book.AuthorInstitution and it will be the value of that object.

How should I store attributes in a many to many relation in sails.js?

I currently created the following tables in my sails project, I have a table named "Users" and a table named "Attendance". These tables are stored in a MySQL database.
Each User can be part of many Attendances and each Attendance can have many users.
This is achieved with the following code:
Attendance model:
attributes: {
attendees: {
collection: 'user',
via: 'attendancelist',
dominant: true
}
User model:
attributes{
attendancelist: {
collection: 'attendance',
via: 'attendees'
}
}
This will create a table named: attendence_attendees__user_attendancelist
Now I would like the store the "Status" of each User. The status will be a bool describing if the user on the Attendance was present or not.
I would like to store the status of each user in the "attendence_attendees__user_attendancelist" table.
Is this possible or is there a better way to store the status ?
I managed to create a custom associative table but now sails no longer adds data to the associative table. I use the following code:
//Attendance model
attributes: {
attendees: {
collection: 'user',
via: 'attendancelist',
through: 'userattendance'
}
//User model
attributes{
attendancelists: {
collection: 'attendance',
via: 'attendees',
through: 'userattendance'
}
}
//Userattendance model
attributes:{
attendancelist: {
model: 'attendance',
foreignKey: true,
columnName: 'attendance_id'
},
attendees: {
model: 'user',
foreignKey: true,
columnName: 'user_id'
},
status: {
type: 'boolean',
defaultsTo: false
}
}
//controller method to add data:
getGroupAttendees: function(req, res){
Group.findOne({id: req.param('groupID')})
.populateAll()
.exec(function getGroup(err, group){
if(err) return res.negotiate(err);
if(!groep) return res.json(401, {err:'group not found'});
var users = [];
group.attendees.forEach(function(user){
users.push(_.pick(user, ['id']));
})
var attendancelistOBJ = {
planning: 1,
attendees: users
}
Attendance.create(attendancelistOBJ, function agendaCreated(err, lijst){
if(err) return res.negotiate(err);
lijst.save(function(err, lst){
if(err) return next(err);
return res.json(attendancelistOBJ.attendees);
});
});
})
}
The above controller method used to work perfectly with the auto generated associative table, but with the custom one it no longer adds users and lists to the table.
Not sure I follow completely but if you have a many-to-many relationship than you will need an associative table that joins the two tables together and the primary key in that table is a composite key from both the primary keys in the User and Attendance tables. Maybe you could store your status in that associative table.
Does that make sense, and/or help?

Categories