Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sequelize: how to do a WHERE condition on joined table with left outer join

Tags:

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.

Picture of database diagram

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.

like image 213
Steve Avatar asked Sep 23 '16 10:09

Steve


People also ask

How do you use left outer joins?

A left outer join is a method of combining tables. The result includes unmatched rows from only the table that is specified before the LEFT OUTER JOIN clause. If you are joining two tables and want the result set to include unmatched rows from only one table, use a LEFT OUTER JOIN clause or a RIGHT OUTER JOIN clause.

What is difference between left outer join and left join?

There really is no difference between a LEFT JOIN and a LEFT OUTER JOIN. Both versions of the syntax will produce the exact same result in PL/SQL. Some people do recommend including outer in a LEFT JOIN clause so it's clear that you're creating an outer join, but that's entirely optional.

Is Outer join Left or right?

Left outer join includes the unmatched rows from the table which is on the left of the join clause whereas a Right outer join includes the unmatched rows from the table which is on the right of the join clause.


2 Answers

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

like image 99
Dajalmar Gutierrez Avatar answered Oct 13 '22 10:10

Dajalmar Gutierrez


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

like image 30
Adiii Avatar answered Oct 13 '22 09:10

Adiii