作用域

作用域允许你定义一些常见的查询,以方便之后快速的访问它们,作用域可以包含所有普通查询方法的属性,比如 whereinclude 以及 limit 等。

定义:

作用域可以在模型定义时被定义,它们可以是一个查询对象,或者一个可以返回一个查询对象的函数。而对于默认的作用域,只能是一个查询对象。

const Project = sequelize.define('project', {
  // Attributes
}, {
  defaultScope: {
    where: {
      active: true
    }
  },
  scopes: {
    deleted: {
      where: {
        deleted: true
      }
    },
    activeUsers: {
      include: [
        { model: User, where: { active: true }}
      ]
    },
    random: function () {
      return {
        where: {
          someNumber: Math.random()
        }
      }
    },
    accessLevel: function (value) {
      return {
        where: {
          accessLevel: {
            $gte: value
          }
        }
      }
    }
  }
});

你同样还可以在模型被创建之后,使用 addScope 方法添加作用域:

默认作用域总是可访问的,这表示,你使用 Project.findAll() ,将会创建下面这样的SQL:

SELECT * FROM projects WHERE active = true

默认作用域可以通过 unscope() 或者 scope(null) 重置:

Project.scope('deleted').findAll(); // Removes the default scope
SELECT * FROM projects WHERE deleted = true

同样的,你还可以在一个创建了作用域的模型中, include 另一个作用域,这使得你不再需要创建重复的 includeattributes 或者 where

activeUsers: {
  include: [
    { model: User.scope('active')}
  ]
}

使用

作用域可以通过模型的 .scope 方法调用,该方法接受一个或者多个作用域名称作为参数,.scope 返回的对象具有所有的普通方法:findAllupdatecountdestroy 等:

const DeletedProjects = Project.scope('deleted');

DeletedProjects.findAll();
// some time passes

// let's look for deleted projects again!
DeletedProjects.findAll();

对于函数式作用域定义,如果该函数不接受任何参数,那么就像其它的作用域定义一样直接访问即可,但是如果该作用域定义接受一个参数,那么你可以像下面这样传递参数给它:

Project.scope('random', { method: ['accessLevel', 19]}).findAll();
SELECT * FROM projects WHERE someNumber = 42 AND accessLevel >= 19

合并

多个作用域可以被合并为一个作用域去作用,只需要将多个作用域的名称放在一个数组中,传递给 .scope 方法即可:

// These two are equivalent
Project.scope('deleted', 'activeUsers').findAll();
Project.scope(['deleted', 'activeUsers']).findAll();
SELECT * FROM projects
INNER JOIN users ON projects.userId = users.id
AND users.active = true

如果你想合并使用默认作用域与某一个特定的其它作用域,使用 defaultScope 作为 key 即可访问默认作用域:

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

如果被合并的作用域中存在两个同样的定义,那么后面出现的作用域的设置将覆盖前面的设置,就像 _.assign 一样:

{
  scope1: {
    where: {
      firstName: 'bob',
      age: {
        $gt: 20
      }
    },
    limit: 2
  },
  scope2: {
    where: {
      age: {
        $gt: 30
      }
    },
    limit: 10
  }
}

调用 .scope('scope1', 'scope2') 将得到下面这样的SQL:

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

对于我们直接传递给查询函数的逻辑,同样会覆盖作用域中的相同的设置,或者与不同的设置进行合并:

Project.scope('deleted').findAll({
  where: {
    firstName: 'john'
  }
})
WHERE deleted = true AND firstName = 'john'

关联

Sequelize有两个不同但相关的概念,差异是很微妙的,但是却非常重要:

  • 关联作用域 —— 这允许你设置默认的用于 getting 或者 setting 关联关系的属性,这在实现多态关联是很有用,只有在使用 getset以及 create 的关联模型时,才会在两个模型之间的关联上调用。
  • 已关联模型的作用域 —— 这允许你设置默认的或者其它的作用域,用于查询关联数据,并且,允许你传递一个定义了作用域的模型作为关联关系的一方,这些作用域都同时适用于模型上的查询以及关联查询。

考虑一下这样的场景,现在有两个模型 PostCommentComment 被关联至多个其它模型(比如ImageVideo等),这时,Comment 的关联就是多态的,Comment 有一个名为 commentable 的属性,同时还有一个 commentable_id 表示关联的模型的ID。

this.Post.hasMany(this.Comment, {
  foreignKey: 'commentable_id',
  scope: {
    commentable: 'post'
  }
});

当我们调用 post.getComments() 时,会自动的加上 WHERE commentable = 'post' 查询,同时,当我们添加一些新的 CommentPost 实例时,该 Commentcommentable 会自动的设置为 post

再考虑到 Post 有一个默认的作用域 where: { active: true },该作用域会一直存在于被关联的模型(Post),而没有像 commentable 作用域一样关联至模型,就像 Post.findAll() 会基于默认作用域一样, User.getPosts() 同样会只展示 active Post。

要禁用默认作用域,可以使用 scope: null 来表示:User.getPosts({scope:null}),如果你想合并多个作用域的话,那也可以使用:

User.getPosts({
  scope: ['scope1', 'scope2']
})

如果你想创建一些快捷方式,用于快速访问关联的作用域模型,你可以像下面这样:

const Post = sequelize.define('post', attributes, {
  defaultScope: {
    where: {
      active: true
    }
  },
  scopes: {
    deleted: {
      where: {
        deleted: true
      }
    }
  }
});

User.hasMany(Post); // regular getPosts association
User.hasMany(Post.scope('deleted'), { as: 'deletedPosts' });
User.getPosts(); // WHERE active = true
User.getDeletedPosts(); // WHERE deleted = true

results matching ""

    No results matching ""