11.6 mongoose 操作数据库
了解了 MongoDB 的基本概念以及用法后,我们就要在 Express 中接入并使用了。为了更优雅地在 Node.js 环境下操作 MongoDB,我们还需要一个好用的第三方包 mongoose,首先安装:
$ yarn add mongoose
安装后,在项目中使用 mongoose 提供的 API 来实现一系列数据库相关的操作。
11.6.1 连接数据库
假设已有一台可用的 MongoDB 数据库服务,第一步是创建一个名为 juejin_blogs 数据库,并创建连接该数据库的用户名和密码,信息如下:
- 数据库地址(IP+端口):127.0.0.1:11027。
- 数据库名称:juejin_blogs。
- 数据库用户名:ruidoc。
- 数据库密码:qwertyu123456。
注意:创建数据库用户时需要指定用户角色,角色必须是 “dbOwner”,否则后面可能会遇到权限不够的问题。
安装后在项目下创建一个新文件 config/mongo.js,然后编写一个连接数据库的中间件,代码如下:
// config/mongo.js
const mongoose = require("mongoose");
const connect = (req, res, next) => {
mongoose
.connect("mongodb://127.0.0.1:11027/juejin_blogs", {
user: "ruidoc",
pass: "qwertyu123456",
})
.then(() => {
console.log("数据库连接成功");
next();
})
.catch((err) => {
console.log("数据库连接失败:", err);
res.status(500).send({
message: "数据库连接失败",
});
});
};
module.exports = connect;
代码中的 mongodb://127.0.0.1:11027/juejin_blogs
就是数据库的连接地址,它由固定的 “mongodb://” 协议 + IP 地址 + 端口 + 数据库名称组成。
当连接成功后,进入下一个中间件;如果连接失败,直接响应 500 错误。
完成之后在入口文件 index.js 中加载这个中间件。
const mongoInit = require("./config/mongo");
app.use(mongoInit);
此时重新启动程序,控制台会打印出“数据库连接成功”。
11.6.2 规范文档结构
数据库连接之后,创建集合和文档完全由程序控制,MongoDB 不会限制文档的格式。这种高度的灵活性既有好处,也有弊端,弊端就是我们可能会添加不规范的数据。
为了规范文档的数据格式,mongoose 提供了一个 Schema 的概念,作用是提前设定某个集合中的文档格式(文档有哪些字段以及对字段的约束),类似用 TypeScript 定义 interface 一样。
还是以一个存储日志的集合为例,在创建该集合前,首先用 Schema 定义一个文档结构,方法如下:
const mongoose = require('mongoose')
const logsSchema = new mongoose.Schema({
title: String,
content: {
type: String,
required: true
}
date: {
type: Date,
default: Date.now
},
})
上述 Schema 中定义的文档共有三个字段,每个字段的含义以及约束条件如下:
title
:日志标题,类型是字符串。content
:日志内容,类型是字符串,必填。date
:创建时间,默认为当前时间。
字段的值可以直接是类型(如:String),或者是一个对象,对象中包含了多个约束条件,Schema 支持的常用的约束条件及含义如下:
type
:字段类型,包括 String、Number、Date、Boolean、Array 等常见类型。required
:字段是否必填,值为 true 或 false。default
:字段默认值,设置后字段会自动创建。unique
:字段值是否唯一,设置后多个文档的字段值不能重复。enum
:给定一个数组,字段值必须是数组中的某一个。validate
:函数,自定义验证字段是否满足要求。
现在创建一个日志集合的 Model 模型(mongoose 提供的用于操作集合的类),指定集合名称为 “logs”,并使用该 Schema 约束集合,方法如下:
const LogsModel = mongoose.model("logs", logsSchema);
接下来我们就可以使用 LogsModel 来操作 logs 集合了。当在集合中添加文档时,Schema 会验证传入的数据是否符合约束条件,不符合则拒绝添加,这样就能保证集合中数据的可靠性。
11.6.3 操作文档
文档的增查改删都基于上一步创建的 Model 模型来实现。模型提供了与 MongoDB 一样的数据操作方法,并且有一些优化的快捷方法可供使用。下面使用 LogsModel 来操作 logs 集合。
首先看如何使用 LogsModel 插入文档:
const insert = async (data) => {
let res = await LogsModel.create(data);
console.log(res); // 返回插入后的文档
};
MongoDB 的插入文档方法是 insertOne() 和 insertMany(),mongoose 将这两个方法合并为一个 model.create() 方法,同时支持单文档插入和批量插入。
查询文档还是使用 find() 和 findOne() 方法不变,如下:
const getLogs = async () => {
let res = await LogsModel.find({
_id: ObjectId("507f191e810c19729de860ea"),
});
return res;
};
注意:mongoose 操作中遇到 _id 字段也要用 ObjectId() 方法包裹,但是 Node.js 中并没有提供这个方法,所以我们要自己实现并定义在全局对象 global 下:
const { Types } = require("mongoose");
global.ObjectId = (id) => new Types.ObjectId(id);
聚合查询完全按照 MongoDB 的方法执行即可,两者没有区别。
更新操作的方法名也没有变化,区别是可以不用 $set 操作符直接更新数据,代码如下:
const updateLogs = async () => {
let res = await LogsModel.updateMany(
{
_id: ObjectId("507f191e810c19729de860ea"),
},
{
content: "用户使用支付宝登录",
}
);
};
对于上面根据 ID 查到文档并更新的场景,mongoose 提供了一个快捷方法,其参数 ID 不需要用 ObjectId() 包裹,更方便一写,如下:
let id = "507f191e810c19729de860ea";
let res = await LogsModel.findByIdAndUpdate(id, {
content: "用户使用支付宝登录",
});
同理,对于根据 ID 删除对应文档,mongoose 也提供了一个快捷方法:
let id = "507f191e810c19729de860ea";
let res = await LogsModel.findByIdAndDelete(id);
if (res) {
console.log("删除成功");
}
常用的方法就是这些,了解了基本的增查改删操作,我们就可以进入业务接口开发的环节。
本章小结
本章主要介绍了 API 开发的基础知识。本章首先说明了为什么要使用 Serverless 云开发,然后注册并开通了阿里云的函数计算,编写了第一个云函数。在创建云函数并选择自定义运行时的时候,可以将 Express 框架作为云函数的基础代码。
本章还基于 Express 框架介绍了 API 开发的基础知识(这部分内容是本章的重点),主要包括 Express 框架的使用和 MongoDB 的操作。只有了解了这些基础知识才能进入后面的业务开发环节。