预加载

当你从数据库检索数据时,也想同时获得与之相关联的查询,这被称为预加载.这个基本思路就是当你调用 findfindAll 时使用 include 属性.让我们假设以下设置:

  1. class User extends Model {}
  2. User.init({ name: Sequelize.STRING }, { sequelize, modelName: 'user' })
  3. class Task extends Model {}
  4. Task.init({ name: Sequelize.STRING }, { sequelize, modelName: 'task' })
  5. class Tool extends Model {}
  6. Tool.init({ name: Sequelize.STRING }, { sequelize, modelName: 'tool' })
  7. Task.belongsTo(User)
  8. User.hasMany(Task)
  9. User.hasMany(Tool, { as: 'Instruments' })
  10. sequelize.sync().then(() => {
  11. // 这是我们继续的地方 ...
  12. })

首先,让我们用它们的关联 user 加载所有的 task.

  1. Task.findAll({ include: [ User ] }).then(tasks => {
  2. console.log(JSON.stringify(tasks))
  3. /*
  4. [{
  5. "name": "A Task",
  6. "id": 1,
  7. "createdAt": "2013-03-20T20:31:40.000Z",
  8. "updatedAt": "2013-03-20T20:31:40.000Z",
  9. "userId": 1,
  10. "user": {
  11. "name": "John Doe",
  12. "id": 1,
  13. "createdAt": "2013-03-20T20:31:45.000Z",
  14. "updatedAt": "2013-03-20T20:31:45.000Z"
  15. }
  16. }]
  17. */
  18. })

请注意,访问者(结果实例中的 User 属性)是单数形式,因为关联是一对一的.

接下来的事情:用多对一的关联加载数据!

  1. User.findAll({ include: [ Task ] }).then(users => {
  2. console.log(JSON.stringify(users))
  3. /*
  4. [{
  5. "name": "John Doe",
  6. "id": 1,
  7. "createdAt": "2013-03-20T20:31:45.000Z",
  8. "updatedAt": "2013-03-20T20:31:45.000Z",
  9. "tasks": [{
  10. "name": "A Task",
  11. "id": 1,
  12. "createdAt": "2013-03-20T20:31:40.000Z",
  13. "updatedAt": "2013-03-20T20:31:40.000Z",
  14. "userId": 1
  15. }]
  16. }]
  17. */
  18. })

请注意,访问者(结果实例中的 Tasks 属性)是复数形式,因为关联是多对一的.

如果关联是别名的(使用 as 参数),则在包含模型时必须指定此别名. 注意用户的 Tool 如何被别名为 Instruments. 为了获得正确的权限,你必须指定要加载的模型以及别名:

  1. User.findAll({ include: [{ model: Tool, as: 'Instruments' }] }).then(users => {
  2. console.log(JSON.stringify(users))
  3. /*
  4. [{
  5. "name": "John Doe",
  6. "id": 1,
  7. "createdAt": "2013-03-20T20:31:45.000Z",
  8. "updatedAt": "2013-03-20T20:31:45.000Z",
  9. "Instruments": [{
  10. "name": "Toothpick",
  11. "id": 1,
  12. "createdAt": null,
  13. "updatedAt": null,
  14. "userId": 1
  15. }]
  16. }]
  17. */
  18. })

你还可以通过指定与关联别名匹配的字符串来包含别名:

  1. User.findAll({ include: ['Instruments'] }).then(users => {
  2. console.log(JSON.stringify(users))
  3. /*
  4. [{
  5. "name": "John Doe",
  6. "id": 1,
  7. "createdAt": "2013-03-20T20:31:45.000Z",
  8. "updatedAt": "2013-03-20T20:31:45.000Z",
  9. "Instruments": [{
  10. "name": "Toothpick",
  11. "id": 1,
  12. "createdAt": null,
  13. "updatedAt": null,
  14. "userId": 1
  15. }]
  16. }]
  17. */
  18. })
  19. User.findAll({ include: [{ association: 'Instruments' }] }).then(users => {
  20. console.log(JSON.stringify(users))
  21. /*
  22. [{
  23. "name": "John Doe",
  24. "id": 1,
  25. "createdAt": "2013-03-20T20:31:45.000Z",
  26. "updatedAt": "2013-03-20T20:31:45.000Z",
  27. "Instruments": [{
  28. "name": "Toothpick",
  29. "id": 1,
  30. "createdAt": null,
  31. "updatedAt": null,
  32. "userId": 1
  33. }]
  34. }]
  35. */
  36. })

当预加载时,我们也可以使用 where 过滤关联的模型. 这将返回 Tool 模型中所有与 where 语句匹配的行的User.

  1. User.findAll({
  2. include: [{
  3. model: Tool,
  4. as: 'Instruments',
  5. where: { name: { [Op.like]: '%ooth%' } }
  6. }]
  7. }).then(users => {
  8. console.log(JSON.stringify(users))
  9. /*
  10. [{
  11. "name": "John Doe",
  12. "id": 1,
  13. "createdAt": "2013-03-20T20:31:45.000Z",
  14. "updatedAt": "2013-03-20T20:31:45.000Z",
  15. "Instruments": [{
  16. "name": "Toothpick",
  17. "id": 1,
  18. "createdAt": null,
  19. "updatedAt": null,
  20. "userId": 1
  21. }]
  22. }],
  23. [{
  24. "name": "John Smith",
  25. "id": 2,
  26. "createdAt": "2013-03-20T20:31:45.000Z",
  27. "updatedAt": "2013-03-20T20:31:45.000Z",
  28. "Instruments": [{
  29. "name": "Toothpick",
  30. "id": 1,
  31. "createdAt": null,
  32. "updatedAt": null,
  33. "userId": 1
  34. }]
  35. }],
  36. */
  37. })

当使用 include.where 过滤一个预加载的模型时,include.required 被隐式设置为 true. 这意味着内部联接完成返回具有任何匹配子项的父模型.

使用预加载模型的顶层 WHERE

将模型的 WHERE 条件从 ON 条件的 include 模式移动到顶层,你可以使用 '$nested.column$' 语法:

  1. User.findAll({
  2. where: {
  3. '$Instruments.name$': { [Op.iLike]: '%ooth%' }
  4. },
  5. include: [{
  6. model: Tool,
  7. as: 'Instruments'
  8. }]
  9. }).then(users => {
  10. console.log(JSON.stringify(users));
  11. /*
  12. [{
  13. "name": "John Doe",
  14. "id": 1,
  15. "createdAt": "2013-03-20T20:31:45.000Z",
  16. "updatedAt": "2013-03-20T20:31:45.000Z",
  17. "Instruments": [{
  18. "name": "Toothpick",
  19. "id": 1,
  20. "createdAt": null,
  21. "updatedAt": null,
  22. "userId": 1
  23. }]
  24. }],
  25. [{
  26. "name": "John Smith",
  27. "id": 2,
  28. "createdAt": "2013-03-20T20:31:45.000Z",
  29. "updatedAt": "2013-03-20T20:31:45.000Z",
  30. "Instruments": [{
  31. "name": "Toothpick",
  32. "id": 1,
  33. "createdAt": null,
  34. "updatedAt": null,
  35. "userId": 1
  36. }]
  37. }],
  38. */

包括所有

要包含所有属性,你可以使用 all:true 传递单个对象:

  1. User.findAll({ include: [{ all: true }]});

包括软删除的记录

如果想要加载软删除的记录,可以通过将 include.paranoid 设置为 false 来实现

  1. User.findAll({
  2. include: [{
  3. model: Tool,
  4. where: { name: { [Op.like]: '%ooth%' } },
  5. paranoid: false // query and loads the soft deleted records
  6. }]
  7. });

排序预加载关联

在一对多关系的情况下.

  1. Company.findAll({ include: [ Division ], order: [ [ Division, 'name' ] ] });
  2. Company.findAll({ include: [ Division ], order: [ [ Division, 'name', 'DESC' ] ] });
  3. Company.findAll({
  4. include: [ { model: Division, as: 'Div' } ],
  5. order: [ [ { model: Division, as: 'Div' }, 'name' ] ]
  6. });
  7. Company.findAll({
  8. include: [ { model: Division, as: 'Div' } ],
  9. order: [ [ { model: Division, as: 'Div' }, 'name', 'DESC' ] ]
  10. });
  11. Company.findAll({
  12. include: [ { model: Division, include: [ Department ] } ],
  13. order: [ [ Division, Department, 'name' ] ]
  14. });

在多对多关系的情况下,你还可以通过表中的属性进行排序.

  1. Company.findAll({
  2. include: [ { model: Division, include: [ Department ] } ],
  3. order: [ [ Division, DepartmentDivision, 'name' ] ]
  4. });

嵌套预加载

你可以使用嵌套的预加载来加载相关模型的所有相关模型:

  1. User.findAll({
  2. include: [
  3. {model: Tool, as: 'Instruments', include: [
  4. {model: Teacher, include: [ /* etc */]}
  5. ]}
  6. ]
  7. }).then(users => {
  8. console.log(JSON.stringify(users))
  9. /*
  10. [{
  11. "name": "John Doe",
  12. "id": 1,
  13. "createdAt": "2013-03-20T20:31:45.000Z",
  14. "updatedAt": "2013-03-20T20:31:45.000Z",
  15. "Instruments": [{ // 1:M and N:M association
  16. "name": "Toothpick",
  17. "id": 1,
  18. "createdAt": null,
  19. "updatedAt": null,
  20. "userId": 1,
  21. "Teacher": { // 1:1 association
  22. "name": "Jimi Hendrix"
  23. }
  24. }]
  25. }]
  26. */
  27. })

这将产生一个外连接. 但是,相关模型上的 where 语句将创建一个内部连接,并仅返回具有匹配子模型的实例. 要返回所有父实例,你应该添加 required: false.

  1. User.findAll({
  2. include: [{
  3. model: Tool,
  4. as: 'Instruments',
  5. include: [{
  6. model: Teacher,
  7. where: {
  8. school: "Woodstock Music School"
  9. },
  10. required: false
  11. }]
  12. }]
  13. }).then(users => {
  14. /* ... */
  15. })

以上查询将返回所有用户及其所有乐器,但只会返回与 Woodstock Music School 相关的老师.

包括所有也支持嵌套加载:

  1. User.findAll({ include: [{ all: true, nested: true }]});

使用 right join 进行关联

默认情况下, 关联是使用 left join 加载的, 也就是说, 它仅包括来自父表的记录. 如果您使用的方言支持,可以通过传递 right 属性来将这种行为更改为右连接. 目前, sqlite 支持right joins.

注意: 仅当 required 是 false 时才遵循 right.

  1. User.findAll({
  2. include: [{
  3. model: Tool // 将创建 left join
  4. }]
  5. });
  6. User.findAll({
  7. include: [{
  8. model: Tool,
  9. right: true // 将创建 right join
  10. }]
  11. });
  12. User.findAll({
  13. include: [{
  14. model: Tool,
  15. required: true,
  16. right: true // 没有作用, 将创建一个 inner join
  17. }]
  18. });
  19. User.findAll({
  20. include: [{
  21. model: Tool,
  22. where: { name: { [Op.like]: '%ooth%' } },
  23. right: true // 没有作用, 将创建一个 inner join
  24. }]
  25. });
  26. User.findAll({
  27. include: [{
  28. model: Tool,
  29. where: { name: { [Op.like]: '%ooth%' } },
  30. required: false
  31. right: true // 因为我们设置 `required` 为 false, 这将创建一个 right join
  32. }]
  33. });