几乎所有的 web 应用都有某种类型的后备数据存储。通常,这种数据存储是某种数据库,用于存储从地址和信用卡号到传感器读数和处方信息的所有内容。数据库提供了一种快速访问大量数据的方法。通常有两种类型的数据库——关系数据库和 NoSQL 数据库。本章重点介绍数据库,以及如何从 Node 应用访问它们。更具体地说,探索了 MySQL 关系数据库和 MongoDB NoSQL 数据库。请注意,本章没有提供安装 MySQL 和 MongoDB 的说明。此外,它还假设您已经熟悉结构化查询语言(SQL ),该语言与关系数据库结合使用。
关系数据库
关系数据库由一组表组成。每个表保存一组由数据组成的记录。表中的单个记录被称为行或元组。存储在这些元组中的数据类型是使用模式预定义的。图 14-1 中显示了一个示例表。该表包含个人信息,包括姓名、性别、社会保险号(SSN)以及他们居住的城市和州(为了节省空间,省略了地址等信息)。
图 14-1 。关系数据库中的示例表
用于创建图 14-1 中表格的 SQL CREATE语句如清单 14-1 所示。这个 SQL 命令定义了表的模式,所有元组都必须遵守这个模式。在这种情况下,这个人的社会保险号必须是 11 个字符长(以适应破折号),他们的性别必须是一个字符,他们的居住州必须是两个字符。此人的姓、名和居住城市的长度都不超过 50 个字符。
CREATE TABLE Person (
SSN CHAR(11) NOT NULL,
LastName VARCHAR(50) NOT NULL,
FirstName VARCHAR(50) NOT NULL,
Gender CHAR(1),
City VARCHAR(50) NOT NULL,
State CHAR(2) NOT NULL,
PRIMARY KEY(SSN)
);还要注意,社会保险号被用作表的主键。主键是一个或多个字段,用于确保表中某个元组的唯一性。由于每个人都应该有一个唯一的社会安全号,这使得它成为主键的理想选择。
清单 14-2 中显示的 SQL INSERT语句用于填充人员表。请注意,每个语句中的所有值都符合预定义的模式。如果您要输入一个无效的数据,或者一个已经存在于表中的 SSN,那么数据库管理系统将拒绝插入。
INSERT INTO Person (SSN, LastName, FirstName, Gender, City, State)
VALUES ('123-45-6789', 'Pluck', 'Peter', 'M', 'Pittsburgh', 'PA');
INSERT INTO Person (SSN, LastName, FirstName, Gender, City, State)
VALUES ('234-56-7890', 'Johnson', 'John', 'M', 'San Diego', 'CA');
INSERT INTO Person (SSN, LastName, FirstName, Gender, City, State)
VALUES ('345-67-8901', 'Doe', 'Jane', 'F', 'Las Vegas', 'NV');
INSERT INTO Person (SSN, LastName, FirstName, Gender, City, State)
VALUES ('456-78-9012', 'Doe', 'John', 'M', 'Las Vegas', 'NV');关系数据库试图通过只在一个地方存储数据来消除冗余。如果只需要在一个位置更新和删除数据,则过程会简单得多。去除冗余的过程被称为规范化,导致多个表使用外键相互引用。外键是在不同的表中唯一标识一个元组的一个或多个字段。
对于一个具体的例子,让我们回到我们的示例数据库。它目前有一个表 Person,用于存储个人信息。如果我们也想追踪这些人的车呢?通过在模式中创建额外的列,可以将这些信息存储在 Person 表中。然而,如何处理一个人拥有多辆汽车的情况呢?您必须继续向表中添加额外的 car 字段(car1、car2 等),其中许多字段都是空的(大多数人只有一辆或没有汽车)。更好的替代方法是创建一个单独的车辆表,其中包含汽车信息和一个引用 Person 表的外键。车辆表示例如图 14-2 所示。
图 14-2 。一个简化的车辆表
用于定义车辆表的CREATE语句如清单 14-3 所示,而用于填充车辆表的插入语句如清单 14-4 所示。注意这辆车。SSN·菲尔德提到了这个人。SSN 场。这是一个外键关系,尽管在本例中两个表中的字段具有相同的名称,但这不是必需的。
清单 14-3 。用于创建车辆表的 SQL
CREATE TABLE Vehicle (
SSN CHAR(11) NOT NULL,
VIN INT UNSIGNED NOT NULL,
Type VARCHAR(50) NOT NULL,
Year INT UNSIGNED NOT NULL,
PRIMARY KEY(VIN),
FOREIGN KEY(SSN)
REFERENCES Person(SSN)
);清单 14-4 。用于填充车辆表的 SQL
INSERT INTO Vehicle (SSN, VIN, Type, Year)
VALUES ('123-45-6789', 12345, 'Jeep', 2014);
INSERT INTO Vehicle (SSN, VIN, Type, Year)
VALUES ('234-56-7890', 67890, 'Van', 2010);
INSERT INTO Vehicle (SSN, VIN, Type, Year)
VALUES ('345-67-8901', 54327, 'Truck', 2009);
INSERT INTO Vehicle (SSN, VIN, Type, Year)
VALUES ('123-45-6789', 98032, 'Car', 2006);关系数据库的真正优势之一是能够快速查询信息,即使信息分散在多个表中。这是使用JOIN操作完成的。清单 14-5 的中显示的 SQL SELECT语句使用了一个JOIN操作来选择在拉斯韦加斯拥有汽车的每个人的名字。在这个例子中,people 表中有两个来自拉斯维加斯的人,但是只有一个人拥有汽车。因此,该查询将返回姓名 Jane Doe。
清单 14-5 。涉及JOIN操作的 SQL 查询
SELECT FirstName, LastName FROM Person INNER JOIN Vehicle
WHERE Person.SSN = Vehicle.SSN AND City = 'Las Vegas';MySQL〔t0〕
MySQL 是一个非常流行的关系数据库管理系统。它也是开源的,可以免费获得。它被广泛使用,以至于 LAMP stack 中的 M 代表 MySQL。它已经被用于许多高知名度的项目和网站,如 WordPress、Wikipedia、Google 和 Twitter。本章中的 MySQL 示例使用第三方模块mysql 访问数据库,该模块必须使用清单 14-6 中所示的命令安装。
清单 14-6 。用于安装 mysql 模块的 npm 命令
$ npm install mysql连接到 MySQL
为了访问数据库,您必须首先建立连接。本章中的例子假设 MySQL 运行在您的本地机器上。要建立连接,您应该首先使用createConnection( )方法创建一个连接对象。有两个实现相同最终结果的createConnection( )化身。第一个版本将一个对象作为唯一的参数。此参数包含用于建立连接的参数。创建连接的例子如清单 14-7 所示。该示例创建了一个到 MySQL 数据库 dbname 的连接,该数据库运行在 localhost:3306 上(MySQL 默认端口为 3306,因此通常可以省略该选项)。用户和密码选项通过防止数据库被任意访问来提供安全性。
清单 14-7 。 创建与 MySQL 数据库的连接
var mysql = require("mysql");
var connection = mysql.createConnection({
"host": "localhost",
"port": 3306,
"user": "username",
"password": "secret",
"database": "dbname"
});另一个版本的createConnection( )将一个 MySQL URL 字符串作为唯一的参数。清单 14-8 展示了同样的createConnection( )例子如何被重写以使用一个 URL 字符串。虽然这个版本提供了更简洁的语法,但可读性不如使用对象文字。
清单 14-8 。使用 URL 字符串创建到 MySQL 数据库的连接
var mysql = require("mysql");
var connection =
mysql.createConnection("mysql://username:secret@localhost:3306/dbname");创建连接对象后,下一步是调用它的connect( )方法。该方法采用单个参数,即在连接建立后调用的回调函数。如果连接时发生错误,它将作为回调函数的第一个也是唯一一个参数传递。清单 14-9 展示了建立连接的过程。
清单 14-9 。 使用 connect()方法建立连接
var mysql = require("mysql");
var connection = mysql.createConnection({
"host": "localhost",
"port": 3306,
"user": "username",
"password": "secret",
"database": "dbname"
});
connection.connect(function(error) {
if (error) {
return console.error(error);
}
// Connection successfully established
});连接池
在前面的例子中,每次应用需要访问数据库时,都会建立一个新的连接。但是,如果您提前知道您的应用将需要许多到数据库的频繁连接,那么建立一个可重用的连接池可能会更有效。每次需要新的连接时,应用可以简单地从池中请求一个连接。一旦连接完成了它的目的,它就可以被返回到池中供将来的请求使用。使用createPool( )方法创建一个连接池,如清单 14-10 所示。注意createPool( )和createConnection( )非常相似。createPool( )还支持一些特定于池的附加选项。这些选项在表 14-1 中列出。
清单 14-10 。使用 createPool( )方法创建连接池
var mysql = require("mysql");
var pool = mysql.createPool({
"host": "localhost",
"user": "username",
"password": "secret",
"database": "dbname"
});表 14-1 。createPool()支持的附加选项
|
[计]选项
|
描述
|
| --- | --- |
| createConnection | 创建池连接时使用的函数。这默认为createConnection( )。 |
| connectionLimit | 一次可以创建的最大连接数。如果省略,则默认为 10。 |
| queueLimit | 池可以排队的最大连接请求数。如果该值为零(默认值),则没有限制。如果存在一个极限并且超过了这个极限,那么从createConnection( )返回一个错误。 |
| waitForConnections | 如果这是真的(默认值),那么如果没有可用的连接,请求将被添加到队列中。如果这是假的,那么池将立即回调并返回一个错误。 |
池的getConnection( )方法用于请求连接。该方法将回调函数作为其唯一的参数。回调函数的参数是可能的错误条件和请求的连接对象。如果没有错误发生,那么连接对象将已经处于连接状态,这意味着不需要调用connect( )。清单 14-11 显示了如何从连接池中请求连接。
清单 14-11 。 使用getConnection( )方法从池中请求连接
var mysql = require("mysql");
var pool = mysql.createPool({
"host": "localhost",
"user": "username",
"password": "secret",
"database": "dbname"
});
pool.getConnection(function(error, connection) {
if (error) {
return console.error(error);
}
// Connection available for use
});关闭连接
可以使用end( )和destroy( )方法关闭非池连接。end( )方法优雅地关闭连接,允许任何排队的查询执行。end( )将回调作为唯一参数。清单 14-12 展示了如何使用end( )来关闭一个打开的连接。
清单 14-12 。 打开一个连接,然后使用end( )关闭它
var mysql = require("mysql");
var connection =
mysql.createConnection("mysql://username:secret@localhost/dbname");
connection.connect(function(error) {
if (error) {
return console.error(error);
}
connection.end(function(error) {
if (error) {
return console.error(error);
}
});
});另一方面,destroy( )方法会立即关闭底层套接字,而不管发生了什么。destroy( )的用法如清单 14-13 所示。
清单 14-13 。connection.destroy( )方法的用法
connection.destroy( );使用release( )和destroy( )方法关闭池连接。release( )实际上并不终止连接,而是简单地将它返回到池中供另一个请求使用。或者,使用destroy( )方法来终止一个连接,并将其从池中删除。下次请求新连接时,池将创建一个新连接来替换被破坏的连接。清单 14-14 提供了一个使用release( )方法的例子。
清单 14-14 。 使用release( )方法释放池连接
var mysql = require("mysql");
var pool = mysql.createPool({
"host": "localhost",
"user": "username",
"password": "secret",
"database": "dbname"
});
pool.getConnection(function(error, connection) {
if (error) {
return console.error(error);
}
connection.release( );
});执行查询
你学会了如何打开连接,也学会了如何关闭连接。现在是时候了解在开始和结束之间发生了什么。连接到数据库后,您的应用将执行一个或多个查询。这是使用连接的query( )方法完成的。query( )方法有两个参数——一个要执行的 SQL 字符串和一个回调函数。回调函数的参数是一个可能的错误对象和 SQL 命令的结果。
清单 14-15 显示了一个完整的例子,它创建一个连接池,请求一个连接,在 Person 表上执行一个 SQL 查询,显示结果,然后将连接释放回连接池。结果输出如清单 14-16 所示。
清单 14-15 。在 Person 表上执行查询
var mysql = require("mysql");
var pool = mysql.createPool({
"host": "localhost",
"user": "username",
"password": "secret",
"database": "dbname"
});
pool.getConnection(function(error, connection) {
if (error) {
return console.error(error);
}
var sql = "SELECT * FROM Person";
connection.query(sql, function(error, results) {
if (error) {
return console.error(error);
}
console.log(results);
connection.release( );
});
});清单 14-16 。清单 14-15 中代码的输出
$ node sql-query.js
[ { SSN: '123-45-6789',
LastName: 'Pluck',
FirstName: 'Peter',
Gender: 'M',
City: 'Pittsburgh',
State: 'PA' },
{ SSN: '234-56-7890',
LastName: 'Johnson',
FirstName: 'John',
Gender: 'M',
City: 'San Diego',
State: 'CA' },
{ SSN: '345-67-8901',
LastName: 'Doe',
FirstName: 'Jane',
Gender: 'F',
City: 'Las Vegas',
State: 'NV' },
{ SSN: '456-78-9012',
LastName: 'Doe',
FirstName: 'John',
Gender: 'M',
City: 'Las Vegas',
State: 'NV' } ]注意清单 14-16 中显示的结果被格式化为一个对象数组。这是因为执行的查询是一个SELECT操作。如果操作是不同的类型(UPDATE, INSERT, DELETE,等等),那么结果应该是包含操作信息的单个对象。例如,清单 14-17 中的命令删除了 People 表中的所有个人。产生的对象如清单 14-18 所示。请注意,affectedRows属性被设置为 4,以指示被删除的元组的数量。
清单 14-17 。用于清除人员表的 SQL DELETE命令
DELETE FROM People;清单 14-18 。执行清单 14-17 中的语句时来自query( )的结果对象
{ fieldCount: 0,
affectedRows: 4,
insertId: 0,
serverStatus: 34,
warningCount: 0,
message: '',
protocol41: true,
changedRows: 0 }
注意当向具有自动增量主键的表中插入行时,结果对象的insertId属性非常有用。
NoSQL 数据库
NoSQL 数据库代表了数据库的另一种主要风格。有许多类型的 NoSQL 数据库可用,例如键/值存储、对象存储和文档存储。常见的 NoSQL 特征是缺乏模式、简单的 API 和宽松的一致性模型。NoSQL 数据库的一个共同点是,为了追求更高的性能和可伸缩性,它们放弃了 MySQL 等系统使用的关系数据模型。
关系数据模型擅长使用被称为事务的原子操作来保持数据的一致性。然而,维护数据一致性是以额外开销为代价的。银行等一些应用要求数据绝对正确。毕竟,一家失去客户资金记录的银行不会存在太久。然而,许多应用可以摆脱 NoSQL 数据存储提供的宽松约束。例如,如果一个更新没有立即出现在社交媒体的新闻源上,这并不是世界末日。
蒙戈布〔t0〕
与 Node.js 结合使用的最著名的 NoSQL 数据库之一是 MongoDB,有时简称为 Mongo。Mongo 是一个面向文档的数据库,它将数据存储在 BSON(二进制 JSON)格式的文档中。Mongo 在 Node 应用中的突出使用产生了术语均值堆栈。首字母缩写词 MEAN 指的是由 MongoDB、Express、 AngularJS(一种用于创建单页面应用的前端框架)和 Node.js 组成的流行软件堆栈。Mongo 已被用于许多流行的网络公司,包括易贝、Foursquare 和 Craigslist。
要从 Node 应用中访问 Mongo,需要一个驱动程序。有许多可用的 Mongo 驱动程序,但 Mongoose 是其中最受欢迎的。清单 14-19 显示了用于安装 mongoose 模块的npm命令。
清单 14-19 。 命令用来安装 mongoose 模块
$ npm install mongoose正在连接到 MongoDB
createConnection( )方法用于创建一个新的 MongoDB 连接。这个方法接受一个 MongoDB URL 作为输入参数。清单 14-20 中显示了一个示例 URL,它使用了与前面的 MySQL 示例相同的连接参数。在本例中,username、secret、localhost 和 dbname 分别对应于用户名、密码、服务器主机和数据库名称。
清单 14-20 。 使用 Mongoose 连接到 MongoDB
var mongoose = require("mongoose");
var connection =
mongoose.createConnection("mongodb://username:secret@localhost/dbname");
注意在 MongoDB 中创建连接有多种方式。本书中展示的方法被认为是最灵活的,因为它可以处理任意数量的数据库连接。另一种技术并不简单,但它只适用于单个数据库连接。
一旦建立了连接,connection 对象就会发出一个 open 事件。open 事件处理程序不接受任何参数。清单 14-21 中显示了一个处理程序的例子。请注意,close( )方法也用于终止连接。
清单 14-21 。 一个示例连接打开事件处理程序
var mongoose = require("mongoose");
var connection = mongoose.createConnection("mongodb://localhost/test");
connection.on("open", function( ) {
console.log("Connection established");
connection.close( );
});计划
MongoDB 没有预定义的模式。Mongoose 通过定义模式来帮助定义 Mongo 文档的结构。模式是定义要存储的数据结构的对象。为了说明模式是如何工作的,我们将重新访问 MySQL 部分中的 People 表。清单 14-22 显示了被重构为一个 Mongoose 模式对象的 People 表。在示例的第二行,导入了Schema( )构造函数。Schema( )构造函数接受一个参数,一个包含模式定义的对象。在本例中,所有模式字段都是字符串类型。Schema( )支持的其他数据类型包括Number, Date, Buffer, Boolean, Mixed, Objectid,和Array。
清单 14-22 。创建表示 Person 表的模式
var mongoose = require("mongoose");
var Schema = mongoose.Schema;
var PersonSchema = new Schema({
SSN: String,
LastName: String,
FirstName: String,
Gender: String,
City: String,
State: String
});回想一下,最初的 Person 表被一个使用外键关系的 Vehicle 表引用。在关系数据库的世界里,这是一个好主意。但是,在 MongoDB 世界中,车辆信息可以作为数组直接添加到 Person 模式中。清单 14-23 显示了人车混合动力车的模式。注意,这种方法不需要连接操作。
清单 14-23 。在 MongoDB 模式中组合 Person 和 Vehicle 表
var mongoose = require("mongoose");
var Schema = mongoose.Schema;
var PersonSchema = new Schema({
SSN: String,
LastName: String,
FirstName: String,
Gender: String,
City: String,
State: String,
Vehicles: [{
VIN: Number,
Type: String,
Year: Number
}]
});模型
要使用我们新创建的模式对象,我们必须将它与一个数据库连接相关联。在猫鼬术语中,这种联系被称为模型。要创建一个模型,使用连接对象的model( )方法。这个方法有两个参数,一个表示模型名称的字符串和一个Schema对象。清单 14-24 显示了如何创建一个人模型。该示例将人员模型定义为模块导出,以便于代码重用。
清单 14-24 。以可重用的方式定义人员模型
var mongoose = require("mongoose");
var Schema = mongoose.Schema;
var PersonSchema = new Schema({
SSN: String,
LastName: String,
FirstName: String,
Gender: String,
City: String,
State: String,
Vehicles: [{
VIN: Number,
Type: String,
Year: Number
}]
});
module.exports = {
getModel: function getModel(connection) {
return connection.model("Person", PersonSchema);
}
};因为人员模型在设计时考虑了可重用性,所以它可以很容易地导入到其他文件中,如清单 14-25 所示。这个例子假设模型已经保存在一个名为PersonModel.js的文件中。
清单 14-25 。在另一个文件中导入人员模型
var mongoose = require("mongoose");
var connection = mongoose.createConnection("mongodb://localhost/test");
var Person = require(__dirname + "/PersonModel").getModel(connection);插入数据
使用 Mongoose 模型将数据插入 MongoDB 是一个简单的两步过程。第一步是使用模型构造函数实例化一个对象。基于清单 14-25 中的,构造函数应该是Person( )。创建对象后,您可以像操作任何其他 JavaScript 对象一样操作它。要真正插入数据,调用模型的save( )方法。save( )接受一个可选参数,一个接受错误参数的回调函数。
清单 14-26 中的例子使用清单 14-24 中定义的模型创建了一个人对象。接下来,一个定制的 foo 字段被添加到模块中。最后,使用模型的save( )方法将数据插入数据库。需要注意的一点是,当保存数据时,foo 字段不会持久化。原因是foo不是模型模式的一部分。该模型将阻止向模型中添加额外的数据,但不会确保包含任何缺失的字段。例如,如果省略了LastName字段,插入仍然会顺利进行。
清单 14-26 。使用 Mongoose 将 Person 对象插入 MongoDB
var mongoose = require("mongoose");
var connection = mongoose.createConnection("mongodb://localhost/test");
var Person = require(__dirname + "/PersonModel").getModel(connection);
connection.on("open", function( ) {
var person = new Person({
SSN: "123-45-6789",
LastName: "Pluck",
FirstName: "Peter",
Gender: "M",
City: "Pittsburgh",
State: "PA",
Vehicles: [
{
VIN: 12345,
Type: "Jeep",
Year: 2014
},
{
VIN: 98032,
Type: "Car",
Year: 2006
}
]
});
person.foo = "bar";
person.save(function(error) {
connection.close( );
if (error) {
return console.error(error);
} else {
console.log("Successfully saved!");
}
});
});查询数据
模型有几种执行查询的方法。要从 Mongo 中检索数据,请使用模型对象的find( )方法。传递给find( )的第一个参数是一个定义查询条件的对象。这个论点稍后将被重新讨论。find( )的第二个参数是可选的回调函数。如果存在,回调函数将可能的错误作为第一个参数,查询结果作为第二个参数。
清单 14-27 中的例子使用 Person 模型的find( )方法来选择所有居住在拉斯维加斯的车主。条件对象通过指定城市“拉斯维加斯”来选择所有拉斯维加斯市民。为了进一步细化搜索,我们寻找大小不等于零的车辆数组(意味着这个人至少拥有一辆汽车)。如果没有错误发生,结果将显示在回调函数中。示例输出如清单 14-28 中的所示。
清单 14-27 。在 MongoDB 中查询所有居住在拉斯维加斯的车主
var mongoose = require("mongoose");
var connection = mongoose.createConnection("mongodb://localhost/test");
var Person = require(__dirname + "/PersonModel").getModel(connection);
connection.on("open", function( ) {
Person.find({
City: "Las Vegas",
Vehicles: {
$not: {$size: 0}
}
}, function(error, results) {
connection.close( );
if (error) {
return console.error(error);
}
console.log(results);
});
});$ node mongoose-query
[ { City: 'Las Vegas',
FirstName: 'Jane',
Gender: 'F',
LastName: 'Doe',
SSN: '345-67-8901',
State: 'NV',
__v: 0,
_id: 528190b19e13b00000000007,
Vehicles:
[ { VIN: 54327,
Type: 'Truck',
Year: 2009,
_id: 528190b19e13b00000000008 } ] } ]查询构建器方法
如果没有向find( )提供回调函数,则返回查询对象。这个查询对象提供了一个查询构建器接口,允许通过使用助手方法将函数调用链接在一起来构建更复杂的查询。在表 14-2 中讨论了其中一些辅助功能。
表 14-2 。各种查询生成器助手方法
|
方法
|
描述
|
| --- | --- |
| where() | 创建附加的搜索细化。这类似于 SQL WHERE子句。 |
| limit() | 接受一个整数参数,该参数指定要返回的最大结果数。 |
| sort() | 根据某些标准对结果进行排序。这类似于 SQL ORDER BY子句。 | |
| select() | 返回已选定字段的子集。 | |
| exec() | 执行查询并调用回调函数。 |
清单 14-29 中显示了一个示例查询生成器。在这个示例中,find( )方法用于选择来自拉斯维加斯的所有个人。然后使用where( )和equals( )方法将搜索进一步细化到姓氏为 Doe 的个人。接下来,使用limit( )方法确保最多选择 10 个人。然后使用sort( )方法按姓氏对结果进行排序,然后按名字进行逆序排序。接下来,使用select( )方法从结果中只提取名字和姓氏字段。最后,执行查询并打印结果。这个特定的查询将从我们的示例数据库中返回 John 和 Jane Doe。
清单 14-29 。查询生成器的一个示例
var mongoose = require("mongoose");
var connection = mongoose.createConnection("mongodb://localhost/test");
var Person = require(__dirname + "/PersonModel").getModel(connection);
connection.on("open", function( ) {
Person.find({
City: "Las Vegas"
})
.where("LastName").equals("Doe")
.limit(10)
.sort("LastName -FirstName")
.select("FirstName LastName")
.exec(function(error, results) {
connection.close( );
if (error) {
return console.error(error);
}
console.log(results);
});
});更新数据
在 Mongoose 中,使用模型的update( )方法更新数据。update( )接受两个必需的参数,后跟两个可选的参数。第一个参数是用于指定更新条件的对象。该对象的行为类似于传递给find( )的对象。update( )的第二个参数是执行实际更新操作的对象。可选的第三个参数是用于传入选项的另一个对象。update( )支持的选项汇总在表 14-3 中。最后一个参数是一个可选的回调函数,它有三个参数。这些参数是一个错误、更新的 Mongo 文档的数量以及 Mongo 返回的原始响应。
表 14-3 。update()支持的选项
|
[计]选项
|
描述
|
| --- | --- |
| safe | 这是一个设置安全模式值的布尔值。如果未指定,则默认为模式中设置的值(true)。如果这是真的,那么发生的任何错误都被传递给回调函数。 |
| upsert | 如果是true,则不存在的文档会被创建。这默认为false。 |
| multi | 如果true,一次操作可以更新多个文档。这默认为false。 |
| strict | 这是为更新设置严格选项的布尔值。如果 strict 为 true,则非模式数据不会写入文档。这默认为false,意味着无关数据将不会持续。 |
清单 14-30 中的例子对居住城市为拉斯维加斯的所有人执行更新操作。第二个参数将他们的居住城市更新为纽约。第三个参数将multi选项设置为true,这意味着可以使用一个操作更新多个文档。回调函数检查错误,然后显示受影响文档的数量和从 Mongo 收到的响应。
清单 14-30 。将所有拉斯维加斯市民转移到纽约的更新
var mongoose = require("mongoose");
var connection = mongoose.createConnection("mongodb://localhost/test");
var Person = require(__dirname + "/PersonModel").getModel(connection);
connection.on("open", function( ) {
Person.update({
City: "Las Vegas"
}, {
City: "New York"
}, {
multi: true
}, function(error, numberAffected, rawResponse) {
connection.close( );
if (error) {
return console.error(error);
}
console.log(numberAffected + " documents affected");
console.log(rawResponse);
});
});删除数据
要使用模型删除数据,请使用模型的remove( )方法。remove( )需要两个参数。第一个参数是指定移除条件的对象。这个对象的工作方式类似于传递给find()的对象。第二个参数是一个可选的回调函数,在执行删除后调用。清单 14-31 中的显示了一个移除居住在圣地亚哥的人的例子。当执行此代码时,它将显示数字 1,对应于删除的项目数。
清单 14-31 。使用 MongoDB 模型删除数据
var mongoose = require("mongoose");
var connection = mongoose.createConnection("mongodb://localhost/test");
var Person = require(__dirname + "/PersonModel").getModel(connection);
connection.on("open", function( ) {
Person.remove({
City: "San Diego"
}, function(error, response) {
connection.close( );
if (error) {
return console.error(error);
}
console.log(response);
});
});摘要
本章向您展示了如何在 Node.js 中使用数据库。在非常简要地概述了关系数据库之后,我们继续讨论 MySQL 数据库。通过介绍mysql模块,您学习了如何与现存的最流行的关系数据库之一进行交互。接下来,本章将重点转向 NoSQL 类的数据存储。近年来,这些数据库变得越来越流行,因为它们比关系数据库更简单,性能更好。在所有可用的 NoSQL 数据库中,本章选择关注 MongoDB,因为它是日益流行的 MEAN stack 的一部分。为了使用 Mongo,我们转向了 mongoose 模块。当然,我们不可能在一章中涵盖所有数据库(甚至 MySQL 和 Mongo 的每个细节),但是通过理解核心概念,您应该能够将您在这里学到的知识应用到其他系统中。

