OK. So, first of all, let's load all tasks with their associated user.
Task.findAll({ include: [ User ] }).then(tasks => {
console.log(JSON.stringify(tasks))
/*
[{
"name": "A Task",
"id": 1,
"createdAt": "2013-03-20T20:31:40.000Z",
"updatedAt": "2013-03-20T20:31:40.000Z",
"userId": 1,
"user": {
"name": "John Doe",
"id": 1,
"createdAt": "2013-03-20T20:31:45.000Z",
"updatedAt": "2013-03-20T20:31:45.000Z"
}
}]
*/
})
Notice that the accessor (the User
property in the resulting instance) is singular because the association is one-to-something.
Next thing: Loading of data with many-to-something associations!
User.findAll({ include: [ Task ] }).then(users => {
console.log(JSON.stringify(users))
/*
[{
"name": "John Doe",
"id": 1,
"createdAt": "2013-03-20T20:31:45.000Z",
"updatedAt": "2013-03-20T20:31:45.000Z",
"tasks": [{
"name": "A Task",
"id": 1,
"createdAt": "2013-03-20T20:31:40.000Z",
"updatedAt": "2013-03-20T20:31:40.000Z",
"userId": 1
}]
}]
*/
})
Notice that the accessor (the Tasks
property in the resulting instance) is plural because the association is many-to-something.
If an association is aliased (using the as
option), you must specify this alias when including the model. Notice how the user's Tool
s are aliased as Instruments
above. In order to get that right you have to specify the model you want to load, as well as the alias:
User.findAll({ include: [{ model: Tool, as: 'Instruments' }] }).then(users => {
console.log(JSON.stringify(users))
/*
[{
"name": "John Doe",
"id": 1,
"createdAt": "2013-03-20T20:31:45.000Z",
"updatedAt": "2013-03-20T20:31:45.000Z",
"Instruments": [{
"name": "Toothpick",
"id": 1,
"createdAt": null,
"updatedAt": null,
"userId": 1
}]
}]
*/
})
User.findAll({ include: ['Instruments'] }).then(users => {
console.log(JSON.stringify(users))
/*
[{
"name": "John Doe",
"id": 1,
"createdAt": "2013-03-20T20:31:45.000Z",
"updatedAt": "2013-03-20T20:31:45.000Z",
"Instruments": [{
"name": "Toothpick",
"id": 1,
"createdAt": null,
"updatedAt": null,
"userId": 1
}]
}]
*/
User.findAll({ include: [{ association: 'Instruments' }] }).then(users => {
console.log(JSON.stringify(users))
[{
"name": "John Doe",
"id": 1,
"createdAt": "2013-03-20T20:31:45.000Z",
"updatedAt": "2013-03-20T20:31:45.000Z",
"Instruments": [{
"name": "Toothpick",
"id": 1,
"createdAt": null,
"updatedAt": null,
"userId": 1
}]
}]
*/
})
When eager loading we can also filter the associated model using where
. This will return all User
s in which the where
clause of Tool
model matches rows.
When an eager loaded model is filtered using include.where
then include.required
is implicitly set totrue
. This means that an inner join is done returning parent models with any matching children.
To move the where conditions from an included model from the ON
condition to the top level WHERE
you can use the '$nested.column$'
syntax:
User.findAll({
where: {
'$Instruments.name$': { [Op.iLike]: '%ooth%' }
},
include: [{
model: Tool,
as: 'Instruments'
}]
}).then(users => {
console.log(JSON.stringify(users));
/*
[{
"name": "John Doe",
"id": 1,
"createdAt": "2013-03-20T20:31:45.000Z",
"updatedAt": "2013-03-20T20:31:45.000Z",
"Instruments": [{
"name": "Toothpick",
"id": 1,
"createdAt": null,
"updatedAt": null,
"userId": 1
}]
}],
[{
"name": "John Smith",
"id": 2,
"createdAt": "2013-03-20T20:31:45.000Z",
"updatedAt": "2013-03-20T20:31:45.000Z",
"Instruments": [{
"name": "Toothpick",
"id": 1,
"createdAt": null,
"updatedAt": null,
"userId": 1
}]
}],
*/
Including everything
To include all attributes, you can pass a single object with all: true
:
User.findAll({ include: [{ all: true }]});
In case you want to eager load soft deleted records you can do that by setting include.paranoid
to false
User.findAll({
include: [{
model: Tool,
where: { name: { [Op.like]: '%ooth%' } },
paranoid: false // query and loads the soft deleted records
}]
});
Ordering Eager Loaded Associations
Company.findAll({ include: [ Division ], order: [ [ Division, 'name' ] ] });
Company.findAll({ include: [ Division ], order: [ [ Division, 'name', 'DESC' ] ] });
Company.findAll({
include: [ { model: Division, as: 'Div' } ],
order: [ [ { model: Division, as: 'Div' }, 'name' ] ]
});
Company.findAll({
include: [ { model: Division, as: 'Div' } ],
order: [ [ { model: Division, as: 'Div' }, 'name', 'DESC' ] ]
});
Company.findAll({
include: [ { model: Division, include: [ Department ] } ],
order: [ [ Division, Department, 'name' ] ]
In the case of many-to-many joins, you are also able to sort by attributes in the through table.
You can use nested eager loading to load all related models of a related model:
User.findAll({
include: [
{model: Tool, as: 'Instruments', include: [
{model: Teacher, include: [ /* etc */]}
]}
]
}).then(users => {
console.log(JSON.stringify(users))
/*
[{
"name": "John Doe",
"id": 1,
"createdAt": "2013-03-20T20:31:45.000Z",
"updatedAt": "2013-03-20T20:31:45.000Z",
"Instruments": [{ // 1:M and N:M association
"name": "Toothpick",
"id": 1,
"createdAt": null,
"updatedAt": null,
"userId": 1,
"Teacher": { // 1:1 association
"name": "Jimi Hendrix"
}
}]
}]
*/
})
This will produce an outer join. However, a where
clause on a related model will create an inner join and return only the instances that have matching sub-models. To return all parent instances, you should add required: false
.
User.findAll({
include: [{
model: Tool,
as: 'Instruments',
include: [{
model: Teacher,
where: {
school: "Woodstock Music School"
},
required: false
}]
}]
}).then(users => {
/* ... */
})
The query above will return all users, and all their instruments, but only those teachers associated with Woodstock Music School
.
Include all also supports nested loading:
User.findAll({ include: [{ all: true, nested: true }]});
Use right join for association
Note: right
is only respected if required
is false.
User.findAll({
include: [{
model: Tool // will create a left join
}]
});
User.findAll({
include: [{
model: Tool,
right: true // will create a right join
}]
});
User.findAll({
include: [{
model: Tool,
required: true,
right: true // has no effect, will create an inner join
}]
});
User.findAll({
include: [{
model: Tool,
where: { name: { [Op.like]: '%ooth%' } },
right: true // has no effect, will create an inner join
}]
});
User.findAll({
include: [{
model: Tool,
where: { name: { [Op.like]: '%ooth%' } },
required: false
right: true // because we set `required` to false, this will create a right join
}]