I'm trying to get all users from a m:n association who is not a member of a specific group.
After a couple hours of trying and searching for something related this is what I came up with. But it doesn't solve my problem 100%
router.get('/:group_id/notmembers', function(req, res) {
models.User.findAll({
include: [
{ model: models.Group,
through: {
model: models.User_Group,
where: {
GroupId: {[Op.notIn] : [req.params.group_id] },
},
},
required: true,
}
],
}).then(function(groups) {
res.send(groups);
});
});
The code I've written so far returns all users who are part of another group instead of the specific group_id, this would work in a scenario where a user could only be member of one group. In my scenario users can be a member of multiple groups, therefore this doesn't work for me.
any help appreciated!
edit:
No one knows?
After blood sweat and tears I finally figured it out.
by setting the required variable to false I can include all db entries for the specific group_id, even if the users don't have it. From there I just select all entries where there is not a Group.id leaving only the users who aren't part of that specific group left.
router.get('/:group_id/notmembers', function(req, res) {
models.User.findAll({
include: [
{ model: models.Group,
through: {
model: models.User_Group,
where: {
GroupId: req.params.group_id,
},
},
required: false,
}
],
where: {
'$Groups.id$' : null,
}
}).then(function(groups) {
res.send(groups);
});
});
Related
I have the following code which returns customers. The problem is that I get results only for customers that have placed an order and have created an address.
CustomerModel.findAll({
attributes: {
include: [
[db.sequelize.fn("COUNT", db.sequelize.col("orders.id")), "orderCount"],
[db.sequelize.literal(`(
SELECT o.createdDate
FROM orders AS o
WHERE
o.customerId = customer.id
ORDER BY o.createdDate DESC
LIMIT 1
)`),
'latestOrderDate'],
[db.sequelize.fn('sum', db.sequelize.col('orders.amount')), 'totalAmount'],
]
},
include: [
{ model: UserModel, as: 'user' },
{ model: AddressModel, required:false },
{ model: OrderModel, attributes: [], required:false }
]
})
Is it possible to get all customers regardless of having an order or an Address?
Thank you!
I ended up changing the two sequelize.fn to literal and it worked like a charm!
this below Sequelize work fine for me without using order, i'm wondering why i can't use order for root table as posts model? when i use this below code i get this error:
Unhandled rejection Error: 'posts' in order / group clause is not valid association
but that work fine on other models such as channelVideoContainer
models.posts.findAll({
where: {
channelId: 1
},
include: [
{
model: models.channelVideoContainer,
include: [models.fileServerSetting]
}, {
model: models.channelMusicContainer,
include: [models.fileServerSetting]
}, {
model: models.channelImageWithTextContainer,
include: [models.fileServerSetting]
}, {
model: models.channelFilesContainer,
include: [models.fileServerSetting]
},
models.channelPlainTextContainer
], order: [
[{model: models.posts}, 'id', 'DESC'],
], limit: 5
}).then(function (result) {
console.log(JSON.stringify(result));
});
You are getting this error because you are querying the posts table/model, and then sorting by a column on the posts model, however you are specifying a "joined" table in your order. This works for your other models because they are in fact joined (using the include option). Since you are querying the posts model you just need to pass in the name of the column you want to order by. See some of the ORDER examples in the documentation.
// just specify the 'id' column, 'post' is assumed because it is the queried Model
order: [['id', 'DESC']],
As a side note, you may want to specify required: false on your include'd models to perform a LEFT JOIN so that rows come back even if there are no matches in the joined table. If you know that rows will be returned (or they are actually required) then leave it as is.
{
model: models.channelFilesContainer,
include: [models.fileServerSetting],
required: false, // LEFT JOIN the channelFilesContainer model
},
My database model is as follows:
An employee drives one or zero vehicles
A vehicle can be driven by one or more employees
A vehicle has a model type that tells us it's fuel type amongst other things.
I'd like sequelize to fetch me all employees where they don't drive a vehicle, or if they do then the vehicle is not diesel.
So where VehicleID is null OR Vehicle.VehicleModel.IsDiesel = false
My current code is as follows:
var employee = sequelize.define('employee', {
ID: Sequelize.INTEGER,
VehicleID: Sequelize.INTEGER
});
var vehicle = sequelize.define('vehicle', {
ID: Sequelize.INTEGER,
ModelID: Sequelize.INTEGER
});
var vehicleModel = sequelize.define('vehicleModel', {
ID: Sequelize.INTEGER,
IsDiesel: Sequelize.BOOLEAN
});
employee.belongsTo(vehicle);
vehicle.belongsTo(vehicleModel);
If I run the following:
options.include = [{
model: model.Vehicle,
attributes: ['ID', 'ModelID'],
include: [
{
model: model.VehicleModel,
attributes: ['ID', 'IsDiesel']
}]
}];
employee
.findAll(options)
.success(function(results) {
// do stuff
});
Sequelize does a left outer join to get me the included tables. So I get employees who drive vehicles and who don't.
As soon as I add a where to my options:
options.include = [{
model: model.Vehicle,
attributes: ['ID', 'ModelID'],
include: [
{
model: model.VehicleModel,
attributes: ['ID', 'IsDiesel']
where: {
IsDiesel: false
}
}]
}];
Sequelize now does an inner join to get the included tables.
This means that I only get employees who drive a vehicle and the vehicle is not diesel. The employees who don't drive a vehicle are excluded.
Fundamentally, I need a way of telling Sequelize to do a left outer join and at the same time have a where condition that states the column from the joined table is false or null.
EDIT:
It turns out that the solution was to use required: false, as below:
options.include = [{
model: model.Vehicle,
attributes: ['ID', 'ModelID'],
include: [
{
model: model.VehicleModel,
attributes: ['ID', 'IsDiesel']
where: {
IsDiesel: false
},
required: false
}],
required: false
}];
I had already tried putting the first 'required:false' but I missed out on putting the inner one. I thought it wasn't working so I gave up on that approach. Dajalmar Gutierrez's answer made me realise I needed both for it to work.
When you add a where clause, sequelize automatically adds a required: true clause to your code.
Adding required: false to your include segment should solve the problem
Note: you should check this issue iss4019
Eager loading
When you are retrieving data from the database there is a fair chance that you also want to get associations with the same query - this is called eager loading. The basic idea behind that, is the use of the attribute include when you are calling find or findAll.
when you set
required: false
will do
LEFT OUTER JOIN
when
required: true
will do
INNER JOIN
for more detail docs.sequelizejs eager-loading
I have two tables (users and games) joined by an association table (game_players), creating a many-to-many relationship:
models.Game.belongsToMany(models.User, { through: models.GamePlayer, as: 'players' });
models.User.belongsToMany(models.Game, { through: models.GamePlayer, foreignKey: 'user_id' });
In addition to the foreign keys user_id and game_id, game_players has a few extra columns for link-specific data:
sequelize.define('game_player', {
isReady: {
defaultValue: false,
type: Sequelize.BOOLEAN,
field: 'is_ready'
},
isDisabled: {
defaultValue: false,
type: Sequelize.BOOLEAN,
field: 'is_disabled'
},
powerPreferences: {
type: Sequelize.TEXT,
field: 'power_preferences'
},
power: {
type: Sequelize.STRING(2),
defaultValue: '?'
}
}, {
underscored: true
});
Suppose I want to fetch a game and eagerly load active players. This was my first effort:
db.models.Game.findAll({
include: [{
model: db.models.User,
as: 'players',
where: { 'game_player.isDisabled': false }
}]
}).nodeify(cb);
This generates the following SQL, which throws the error Column players.game_player.isDisabled does not exist:
SELECT "game"."id",
"game"."name",
"game"."description",
"game"."variant",
"game"."status",
"game"."move_clock" AS "moveClock",
"game"."retreat_clock" AS "retreatClock",
"game"."adjust_clock" AS "adjustClock",
"game"."max_players" AS "maxPlayers",
"game"."created_at",
"game"."updated_at",
"game"."gm_id",
"game"."current_phase_id",
"players"."id" AS "players.id",
"players"."email" AS "players.email",
"players"."temp_email" AS "players.tempEmail",
"players"."password" AS "players.password",
"players"."password_salt" AS "players.passwordSalt",
"players"."action_count" AS "players.actionCount",
"players"."failed_action_count" AS "players.failedActionCount",
"players"."created_at" AS "players.created_at",
"players"."updated_at" AS "players.updated_at",
"players.game_player"."is_ready" AS
"players.game_player.isReady",
"players.game_player"."is_disabled" AS
"players.game_player.isDisabled",
"players.game_player"."power_preferences" AS
"players.game_player.powerPreferences",
"players.game_player"."power" AS "players.game_player.power",
"players.game_player"."created_at" AS
"players.game_player.created_at",
"players.game_player"."updated_at" AS
"players.game_player.updated_at",
"players.game_player"."game_id" AS
"players.game_player.game_id",
"players.game_player"."user_id" AS
"players.game_player.user_id"
FROM "games" AS "game"
INNER JOIN ("game_players" AS "players.game_player"
INNER JOIN "users" AS "players"
ON "players"."id" = "players.game_player"."user_id")
ON "game"."id" = "players.game_player"."game_id"
AND "players"."game_player.isdisabled" = false;
Clearly Sequelize is wrapping my constraint alias with incorrect quotes: 'players'.'game_player.isdisabled' should be 'players.game_player'.isdisabled. How can I revise my Sequelize code above to correctly query this column?
I got it, but only through manually browsing the repository's closed tickets and coming upon #4880.
Clauses using joined table columns that don't work out of the box can be wrapped in $. I honestly don't understand its magic, because I swear I don't see any documentation for it. Modifying my query above achieved what I wanted:
db.models.Game.findAll({
include: [{
model: db.models.User,
as: 'players',
where: { '$players.game_player.is_disabled$': false }
}]
}).nodeify(cb);
After searching around, I found that through.where can also be used:
db.models.Game.findAll({
include: [{
model: db.models.User,
as: 'players',
through: { where: { isDisabled: false } }
}]
})
Reference:
Is it possible to filter a query by the attributes in the association table with sequelize?
Eager loading with Many-to-Many relationships
Your query should be on the join table with the 'where' condition, and then you should use the 'include' clause to include the two other models, like this:
db.models.GamePlayer.findAll({
where: {isDisabled: false},
attributes: [],
include: [models.User, models.Game]
}).then(function(result){
....
});
I am trying to get all of my posts' comments which comments count is greater than 0. I am tying to add the where in find and populate, but none of it worked.
Post model:
module.exports = {
attributes: {
user: {
model: 'user'
},
comments: {
collection: 'comment',
via: 'post'
}
};
PostController:
Post
.find({comments: {'>': 0}, user: me})
.populate('comments')
.exec(function(err, comments){
res.json(comments);
});
In current Sails/Waterline relations implementation you can't filter by one-to-many related fields. You need to filter the result after find is completed.