查询
find
– 搜索数据库中一个特定元素
// 根据已知ID查询
Project.findByPk(123).then(project => {
// project 会是 Project 的实例,而且为表中 id 为 123 的存储内容
// 如果未定义此类条目,则将为 null
})
// 根据属性查询
Project.findOne({ where: {title: 'aProject'} }).then(project => {
// project 会是所匹配到的第一条`title`为'aProject'的 Project || null
})
Project.findOne({
where: {title: 'aProject'},
attributes: ['id', ['name', 'title']]
}).then(project => {
// project 会是所匹配到的第一条`title`为'aProject'的 Project || null
// project.get('title') 将包含该项目的名称
})
复制代码
findOrCreate
– 搜索一个特定元素不存在时则新建
findOrCreate
方法可用于检查数据库中是否已存在某个元素。如果已存在,则返回相应的实例。 如果该元素不存在,则将创建它。
假设我们有一个有User
模型的空数据库,其有一个username
和一个job
属性。
对于创建的情况,可以在where
选项后面添加defaults
。
User
.findOrCreate({where: {username: 'sdepold'}, defaults: {job: 'Technical Lead JavaScript'}})
.then(([user, created]) => {
console.log(user.get({
plain: true
}))
console.log(created)
/*
findOrCreate 会返回一个包含所找到或创建的对象的数组,以及一个布尔值,如果创建了新对象,则该布尔值为true,否则为false:
[ {
username: 'sdepold',
job: 'Technical Lead JavaScript',
id: 1,
createdAt: Fri Mar 22 2013 21: 28: 34 GMT + 0100(CET),
updatedAt: Fri Mar 22 2013 21: 28: 34 GMT + 0100(CET)
},
true ]
In the example above, the array spread on line 3 divides the array into its 2 parts and passes them
as arguments to the callback function defined beginning at line 39, which treats them as "user" and
"created" in this case. (So "user" will be the object from index 0 of the returned array and
"created" will equal "true".)
*/
})
复制代码
findAndCountAll
– 搜索数据库中多个元素,同时返回数据和总数
findAndCountAll
是一个结合了findAll
和count
便捷方法(请参见下文),在处理与分页相关的查询时非常有用。在这一查询中,可以用来检索有limit
和offset
的数据,返回结果除检索数据外还会有记录总数。
本方法查询成功后,将收到有以下两个属性的对象:
count
– 整数,表示由where
子句和其它过滤条件检索到的数据总数rows
– 对象数组,由where
子句和其它过滤条件匹配到的,及limit
和offset
的数据范围内的数据
Project
.findAndCountAll({
where: {
title: {
[Op.like]: 'foo%'
}
},
offset: 10,
limit: 2
})
.then(result => {
console.log(result.count);
console.log(result.rows);
});
复制代码
它还支持include
查询,仅会将标记为required
的包含项添加到计数部分。
如果,在检索用户的同时,同时想查出其概况信息:
User.findAndCountAll({
include: [
{ model: Profile, required: true }
],
limit: 3
});
复制代码
因为Profile
设置了required
,所以结果是内连接查询,也就只有有profile信息的用户才会被统计。如果不设置required
,则不管有没有profile都会被统计。
另外,添加where
条件后,会自动设置required
:
User.findAndCountAll({
include: [
{ model: Profile, where: { active: true }}
],
limit: 3
});
复制代码
在以上查询中,因为添加了where
条件,所以会自动将required
设置为true
。
传给findAndCountAll
的选项参数,与findAll
相同
findAll
– 搜索数据库中多个元素
// 多个元素
Project.findAll().then(projects => {
// 返回数组
})
// 搜索特定属性-哈希用法
Project.findAll({ where: { name: 'A Project' } }).then(projects => {
// 项目将是具有指定名称的项目实例数组
})
// 在特定范围内搜索
Project.findAll({ where: { id: [1,2,3] } }).then(projects => {
// 项目将是id为1、2或3的项目数组
// 这实际上是在进行IN查询
})
Project.findAll({
where: {
id: {
[Op.and]: {a: 5}, // AND (a = 5)
[Op.or]: [{a: 5}, {a: 6}], // (a = 5 OR a = 6)
[Op.gt]: 6, // id > 6
[Op.gte]: 6, // id >= 6
[Op.lt]: 10, // id < 10
[Op.lte]: 10, // id <= 10
[Op.ne]: 20, // id != 20
[Op.between]: [6, 10], // BETWEEN 6 AND 10
[Op.notBetween]: [11, 15], // NOT BETWEEN 11 AND 15
[Op.in]: [1, 2], // IN [1, 2]
[Op.notIn]: [1, 2], // NOT IN [1, 2]
[Op.like]: '%hat', // LIKE '%hat'
[Op.notLike]: '%hat', // NOT LIKE '%hat'
[Op.iLike]: '%hat', // ILIKE '%hat' (case insensitive) (PG only)
[Op.notILike]: '%hat', // NOT ILIKE '%hat' (PG only)
[Op.overlap]: [1, 2], // && [1, 2] (PG array overlap operator)
[Op.contains]: [1, 2], // @> [1, 2] (PG array contains operator)
[Op.contained]: [1, 2], // <@ [1, 2] (PG array contained by operator)
[Op.any]: [2,3] // ANY ARRAY[2, 3]::INTEGER (PG only)
},
status: {
[Op.not]: false // status NOT FALSE
}
}
})
复制代码
复合过滤 / OR / NOT 查询
在具有多层嵌套AND
、OR
和NOT
条件的查询中,可能会很复杂。为了做到这一点,可以使用or
、and
或not
操作符:
Project.findOne({
where: {
name: 'a project',
[Op.or]: [
{ id: [1,2,3] },
{ id: { [Op.gt]: 10 } }
]
}
})
Project.findOne({
where: {
name: 'a project',
id: {
[Op.or]: [
[1,2,3],
{ [Op.gt]: 10 }
]
}
}
})
复制代码
上面两段代码都会生成以下查询语句:
SELECT *
FROM `Projects`
WHERE (
`Projects`.`name` = 'a project'
AND (`Projects`.`id` IN (1,2,3) OR `Projects`.`id` > 10)
)
LIMIT 1;
复制代码
not
示例:
Project.findOne({
where: {
name: 'a project',
[Op.not]: [
{ id: [1,2,3] },
{ array: { [Op.contains]: [3,4,5] } }
]
}
});
复制代码
会生成:
SELECT *
FROM `Projects`
WHERE (
`Projects`.`name` = 'a project'
AND NOT (`Projects`.`id` IN (1,2,3) OR `Projects`.`array` @> ARRAY[3,4,5]::INTEGER[])
)
LIMIT 1;
复制代码
对数据集使用limit
、offset
、order
和group
要获取更多相关数据,可以使用limit
、offset
、order
和group
:
// 限制查询的结果
Project.findAll({ limit: 10 })
// 跨过前10个元素
Project.findAll({ offset: 10 })
// 跨过前10个元素,选择2个
Project.findAll({ offset: 10, limit: 2 })
复制代码
分组和排序的语法相同,以下是一个分组的示例,另一个是排序。
Project.findAll({order: [['title', 'DESC']]})
// yields ORDER BY title DESC
Project.findAll({group: 'name'})
// yields GROUP BY name
复制代码
请注意,在上面的两个示例中,所提供的字符串是直接插入查询中的,即列名不会被转义。当你向排序/分组提供字符串时,总是这样。如果要转义列名,即使只想按单个列排序/分组,也应提供一个参数数组。
something.findOne({
order: [
// will return `name`
['name'],
// will return `username` DESC
['username', 'DESC'],
// will return max(`age`)
sequelize.fn('max', sequelize.col('age')),
// will return max(`age`) DESC
[sequelize.fn('max', sequelize.col('age')), 'DESC'],
// will return otherfunction(`col1`, 12, 'lalala') DESC
[sequelize.fn('otherfunction', sequelize.col('col1'), 12, 'lalala'), 'DESC'],
// will return otherfunction(awesomefunction(`col`)) DESC, This nesting is potentially infinite!
[sequelize.fn('otherfunction', sequelize.fn('awesomefunction', sequelize.col('col'))), 'DESC']
]
})
复制代码
综上所述,order/group数组的元素可以如下:
-
String
– 将被添加引号``
-
Array
– 第一个元素将被添加引号,第二个将被直接追加 -
Object
- Raw将直接添加而不引用
- 其他所有内容都将被忽略,如果未设置
raw
,查询将失败
-
Sequelize.fn
及Sequelize.col
– 返回函数和带引号的列名
原始查询
有时,你可能希望得到一个仅需显示而无需处理的庞大数据集。对于所选择的每一行,Sequelize创建一个实例,该实例具有用于更新、删除、获取关联等功能。如果数据量较大,则可能需要一些时间。如果只需要原始数据并且不想更新任何内容,可以像下面这样直接获取原始数据。
// 如果你需要查询大量数据,而不想为每条数据构建DAO花费时间
// 可以传入一个 `raw` 选项,以获取原始数据:
Project.findAll({ where: { ... }, raw: true })
复制代码
count
– 统计数据库中元素数
这是一个统计数据库对象数的方法:
Project.count().then(c => {
console.log("There are " + c + " projects!")
})
Project.count({ where: {'id': {[Op.gt]: 25}} }).then(c => {
console.log("There are " + c + " projects with an id greater than 25.")
})
复制代码
max
– 获取表中特定属性的最大值
用于获取某一属性的最大值:
/*
Let's assume 3 person objects with an attribute age.
The first one is 10 years old,
the second one is 5 years old,
the third one is 40 years old.
*/
Project.max('age').then(max => {
// this will return 40
})
Project.max('age', { where: { age: { [Op.lt]: 20 } } }).then(max => {
// will be 10
})
复制代码
min
– 获取表中特定属性的最小值
用于获取某一属性的最大值:
/*
Let's assume 3 person objects with an attribute age.
The first one is 10 years old,
the second one is 5 years old,
the third one is 40 years old.
*/
Project.min('age').then(min => {
// this will return 5
})
Project.min('age', { where: { age: { [Op.gt]: 5 } } }).then(min => {
// will be 10
})
复制代码
sum
– 对特定属性的值求和
要计算表中指定列的总和,可以使用sum
方法:
/*
Let's assume 3 person objects with an attribute age.
The first one is 10 years old,
the second one is 5 years old,
the third one is 40 years old.
*/
Project.sum('age').then(sum => {
// this will return 55
})
Project.sum('age', { where: { age: { [Op.gt]: 5 } } }).then(sum => {
// will be 50
})
复制代码
预加载
当从数据库中检索数据时,你可能会希望获得与同一查询的关联项-这称为“预加载”。其背后的基本思想是,在调用find
或findAll
时使用include
属性。假设有以下设置:
class User extends Model {}
User.init({ name: Sequelize.STRING }, { sequelize, modelName: 'user' })
class Task extends Model {}
Task.init({ name: Sequelize.STRING }, { sequelize, modelName: 'task' })
class Tool extends Model {}
Tool.init({ name: Sequelize.STRING }, { sequelize, modelName: 'tool' })
Task.belongsTo(User)
User.hasMany(Task)
User.hasMany(Tool, { as: 'Instruments' })
sequelize.sync().then(() => {
// this is where we continue ...
})
复制代码
现在,让我们来获取所有task及与其相关联的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"
}
}]
*/
})
复制代码
请注意,访问器(在结果实例中为User
属性)是单数的,因为关联是一对一的。
接下来,通过多对多的形式来加载数据:
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
}]
}]
*/
})
复制代码
请注意,访问器(结果实例中的Tasks
属性)是复数形式,因为关联是多对多的。
如果对关联使用别名(通过as
指定),可以在关联模型时指定别名。如,以下示例中对user的Tool
指定别名Instruments
。为了正确处理,必须指定要加载的模型以及别名:
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
}]
}]
*/
})
复制代码
在预加载时,同样可以关联模型使用where
。以下会返回User
的,所有符合where
条件的Tool
模型记录:
User.findAll({
include: [{
model: Tool,
as: 'Instruments',
where: { name: { [Op.like]: '%ooth%' } }
}]
}).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
}]
}],
*/
})
复制代码
如果预加载使用了include.where
过滤,则include.required
会被设置为true
。这意味着,父模型与子模型之间是内连接的关系。
在顶层where
中预加载模型
将where
条件从包含模型从ON
条件移到顶层的WHERE
,可以使用'$nested.column$'
语法:
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
}]
}],
*/
复制代码
包含所有
要包含所有属性,可以将all: true
做为单个对象传入:
User.findAll({ include: [{ all: true }]});
复制代码
包含软删除的记录
如果想加载软删除的记录,可以将include.paranoid
设置为false
:
User.findAll({
include: [{
model: Tool,
where: { name: { [Op.like]: '%ooth%' } },
paranoid: false // query and loads the soft deleted records
}]
});
复制代码
对预加载的关联排序
以下是一个一对多的关系:
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' ] ]
});
复制代码
在这个多对多的连接中,同样可以对关联表排序:
Company.findAll({
include: [ { model: Division, include: [ Department ] } ],
order: [ [ Division, DepartmentDivision, 'name' ] ]
});
复制代码
嵌套预加载
可以使用嵌套的预加载来加载相关模型的所有相关模型:
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"
}
}]
}]
*/
})
复制代码
这会生成一个外连接。但是,相关模型上的where
子句将创建内联接,并且仅返回具有匹配子模型的实例。要返回所有父实例,应添加required: false
。
User.findAll({
include: [{
model: Tool,
as: 'Instruments',
include: [{
model: Teacher,
where: {
school: "Woodstock Music School"
},
required: false
}]
}]
}).then(users => {
/* ... */
})
复制代码
上面的查询将返回所有User及其所有Instrument,但仅返回与Woodstock Music School
相关的那些Teacher。
全部包括还支持嵌套加载:
User.findAll({ include: [{ all: true, nested: true }]});
复制代码
// 查询所有 users
User.findAll().then(users => {
console.log("All users:", JSON.stringify(users, null, 4));
});
// 创建一个新 user
User.create({ firstName: "Jane", lastName: "Doe" }).then(jane => {
console.log("Jane's auto-generated ID:", jane.id);
});
// 删除每个名为 "Jane" 的记录
User.destroy({
where: {
firstName: "Jane"
}
}).then(() => {
console.log("Done");
});
// 修改每个`lastName`为`null`的记录修改为"Doe"
User.update({ lastName: "Doe" }, {
where: {
lastName: null
}
}).then(() => {
console.log("Done");
});
复制代码
数据类型
Sequelize.STRING // VARCHAR(255)
Sequelize.STRING(1234) // VARCHAR(1234)
Sequelize.STRING.BINARY // VARCHAR BINARY
Sequelize.TEXT // TEXT
Sequelize.TEXT('tiny') // TINYTEXT
Sequelize.CITEXT // CITEXT PostgreSQL and SQLite only.
Sequelize.INTEGER // INTEGER
Sequelize.BIGINT // BIGINT
Sequelize.BIGINT(11) // BIGINT(11)
Sequelize.FLOAT // FLOAT
Sequelize.FLOAT(11) // FLOAT(11)
Sequelize.FLOAT(11, 10) // FLOAT(11,10)
Sequelize.REAL // REAL PostgreSQL only.
Sequelize.REAL(11) // REAL(11) PostgreSQL only.
Sequelize.REAL(11, 12) // REAL(11,12) PostgreSQL only.
Sequelize.DOUBLE // DOUBLE
Sequelize.DOUBLE(11) // DOUBLE(11)
Sequelize.DOUBLE(11, 10) // DOUBLE(11,10)
Sequelize.DECIMAL // DECIMAL
Sequelize.DECIMAL(10, 2) // DECIMAL(10,2)
Sequelize.DATE // DATETIME for mysql / sqlite, TIMESTAMP WITH TIME ZONE for postgres
Sequelize.DATE(6) // DATETIME(6) for mysql 5.6.4+. Fractional seconds support with up to 6 digits of precision
Sequelize.DATEONLY // DATE without time.
Sequelize.BOOLEAN // TINYINT(1)
Sequelize.ENUM('value 1', 'value 2') // An ENUM with allowed values 'value 1' and 'value 2'
Sequelize.ARRAY(Sequelize.TEXT) // Defines an array. PostgreSQL only.
Sequelize.ARRAY(Sequelize.ENUM) // Defines an array of ENUM. PostgreSQL only.
Sequelize.JSON // JSON column. PostgreSQL, SQLite and MySQL only.
Sequelize.JSONB // JSONB column. PostgreSQL only.
Sequelize.BLOB // BLOB (bytea for PostgreSQL)
Sequelize.BLOB('tiny') // TINYBLOB (bytea for PostgreSQL. Other options are medium and long)
Sequelize.UUID // UUID datatype for PostgreSQL and SQLite, CHAR(36) BINARY for MySQL (use defaultValue: Sequelize.UUIDV1 or Sequelize.UUIDV4 to make sequelize generate the ids automatically)
Sequelize.CIDR // CIDR datatype for PostgreSQL
Sequelize.INET // INET datatype for PostgreSQL
Sequelize.MACADDR // MACADDR datatype for PostgreSQL
Sequelize.RANGE(Sequelize.INTEGER) // Defines int4range range. PostgreSQL only.
Sequelize.RANGE(Sequelize.BIGINT) // Defined int8range range. PostgreSQL only.
Sequelize.RANGE(Sequelize.DATE) // Defines tstzrange range. PostgreSQL only.
Sequelize.RANGE(Sequelize.DATEONLY) // Defines daterange range. PostgreSQL only.
Sequelize.RANGE(Sequelize.DECIMAL) // Defines numrange range. PostgreSQL only.
Sequelize.ARRAY(Sequelize.RANGE(Sequelize.DATE)) // Defines array of tstzrange ranges. PostgreSQL only.
Sequelize.GEOMETRY // Spatial column. PostgreSQL (with PostGIS) or MySQL only.
Sequelize.GEOMETRY('POINT') // Spatial column with geometry type. PostgreSQL (with PostGIS) or MySQL only.
Sequelize.GEOMETRY('POINT', 4326) // Spatial column with geometry type and SRID. PostgreSQL (with PostGIS) or MySQL only.
复制代码