合并

通过将作用域数组传递到 .scope 或通过将作用域作为连续参数传递,可以同时应用多个作用域.

  1. // 这两个是等价的
  2. Project.scope('deleted', 'activeUsers').findAll();
  3. Project.scope(['deleted', 'activeUsers']).findAll();
  1. SELECT * FROM projects
  2. INNER JOIN users ON projects.userId = users.id
  3. WHERE projects.deleted = true
  4. AND users.active = true

如果要将其他作用域与默认作用域一起应用,请将键 defaultScope 传递给 .scope:

  1. Project.scope('defaultScope', 'deleted').findAll();
  1. SELECT * FROM projects WHERE active = true AND deleted = true

当调用多个作用域时,后续作用域的键将覆盖以前的作用域(类似于 Object.assign),除了whereinclude,它们将被合并. 考虑两个作用域:

  1. {
  2. scope1: {
  3. where: {
  4. firstName: 'bob',
  5. age: {
  6. [Op.gt]: 20
  7. }
  8. },
  9. limit: 2
  10. },
  11. scope2: {
  12. where: {
  13. age: {
  14. [Op.gt]: 30
  15. }
  16. },
  17. limit: 10
  18. }
  19. }

调用 .scope('scope1', 'scope2') 将产生以下查询

  1. WHERE firstName = 'bob' AND age > 30 LIMIT 10

注意 scope2 将覆盖 limitage,而 firstName 被保留. limit,offset,order,paranoid,lockraw字段被覆盖,而where被浅层合并(意味着相同的键将被覆盖). include 的合并策略将在后面讨论.

请注意,多个应用作用域的 attributes 键以这样的方式合并,即始终保留 attributes.exclude. 这允许合并多个作用域,并且永远不会泄漏最终作用域内的敏感字段.

将查找对象直接传递给作用域模型上的findAll(和类似的查找程序)时,适用相同的合并逻辑:

  1. Project.scope('deleted').findAll({
  2. where: {
  3. firstName: 'john'
  4. }
  5. })
  1. WHERE deleted = true AND firstName = 'john'

这里的 deleted 作用域与 finder 合并. 如果我们要将 where: { firstName: 'john', deleted: false } 传递给 finder,那么 deleted 作用域将被覆盖.

合并 include

Include 是根据包含的模型递归合并的. 这是一个非常强大的合并,在 v5 上添加,并通过示例更好地理解.

考虑四种模型:Foo,Bar,Baz和Qux,具有如下多种关联:

  1. class Foo extends Model {}
  2. class Bar extends Model {}
  3. class Baz extends Model {}
  4. class Qux extends Model {}
  5. Foo.init({ name: Sequelize.STRING }, { sequelize });
  6. Bar.init({ name: Sequelize.STRING }, { sequelize });
  7. Baz.init({ name: Sequelize.STRING }, { sequelize });
  8. Qux.init({ name: Sequelize.STRING }, { sequelize });
  9. Foo.hasMany(Bar, { foreignKey: 'fooId' });
  10. Bar.hasMany(Baz, { foreignKey: 'barId' });
  11. Baz.hasMany(Qux, { foreignKey: 'bazId' });

现在,考虑Foo上定义的以下四个作用域:

  1. {
  2. includeEverything: {
  3. include: {
  4. model: this.Bar,
  5. include: [{
  6. model: this.Baz,
  7. include: this.Qux
  8. }]
  9. }
  10. },
  11. limitedBars: {
  12. include: [{
  13. model: this.Bar,
  14. limit: 2
  15. }]
  16. },
  17. limitedBazs: {
  18. include: [{
  19. model: this.Bar,
  20. include: [{
  21. model: this.Baz,
  22. limit: 2
  23. }]
  24. }]
  25. },
  26. excludeBazName: {
  27. include: [{
  28. model: this.Bar,
  29. include: [{
  30. model: this.Baz,
  31. attributes: {
  32. exclude: ['name']
  33. }
  34. }]
  35. }]
  36. }
  37. }

这四个作用域可以很容易地深度合并,例如通过调用 Foo.scope('includeEverything', 'limitedBars', 'limitedBazs', 'excludeBazName').findAll(),这完全等同于调用以下内容:

  1. Foo.findAll({
  2. include: {
  3. model: this.Bar,
  4. limit: 2,
  5. include: [{
  6. model: this.Baz,
  7. limit: 2,
  8. attributes: {
  9. exclude: ['name']
  10. },
  11. include: this.Qux
  12. }]
  13. }
  14. });

观察四个作用域如何合并为一个. 根据所包含的模型合并作用域的include. 如果一个作用域包括模型A而另一个作用域包括模型B,则合并结果将包括模型A和B.另一方面,如果两个作用域包括相同的模型A,但具有不同的参数(例如嵌套include或其他属性) ,这些将以递归方式合并,如上所示.

无论应用于作用域的顺序如何,上面说明的合并都以完全相同的方式工作. 如果某个参数由两个不同的作用域设置,那么只会该顺序产生差异 - 这不是上述示例的情况,因为每个作用域都做了不同的事情.

这种合并策略的工作方式与传递给.findAll,.findOne等的参数完全相同.