MongoDB 无疑是当今最受欢迎的 NoSQL 数据库抉择之一,它有一个很棒的社区和生态系统。
在本文中,咱们将介绍在应用 Node.js 设置 MongoDB 和 Mongoose 时应遵循的一些最佳实际。
1. 为什么须要 Mongoose?
为了了解咱们为什么须要 Mongoose,咱们先来理解 MongoDB(也是一个数据库)在架构层面的工作原理。
- 你有一个数据库服务器(例如 MongoDB 社区服务器)
- 你正在运行一个 Node.js 脚本(作为一个过程)
MongoDB 服务器在 TCP 套接字上监听(通常),你的 Node.js 过程能够应用 TCP 连贯来连贯它。
然而在 TCP 之上,MongoDB 也有本人的协定来理解客户端(咱们的 Node.js 过程)到底想要数据库做什么。
对于这种通信,咱们不须要学习咱们在 TCP 层上要发送的音讯,而是借助一个“驱动”软件将其形象掉,在这里称为 MongoDB 驱动。MongoDB 驱动在这里以 npm 包的模式提供。
当初请记住,MongoDB 驱动程序负责连贯和形象你的低层通信申请 / 响应——但作为开发者,这只能让你走到这一步。
因为 MongoDB 是一个无模式数据库,它为你提供了比初学者所需的更多的性能。更大的权力意味着更大的出错可能性,你须要缩小可在代码中制作的谬误和毁坏表的可能性。
Mongoose 是对原生 MongoDB 驱动(我下面提到的 npm 包)的一个形象。
抽象化的个别教训法令(我了解的形式)是,每一个抽象化都会损失一些底层操作能力。但这并不一定意味着它是坏的,有时它能进步生产力 1000 倍以上,因为你基本不须要齐全拜访底层 API。
一个好的思路是,你在技术上用 C 语言和 Python 创立一个实时聊天利用。作为开发人员,Python 的例子将更容易和更快地实现,生产率更高。C 可能更有效率,但它会在生产力、开发速度、谬误、解体方面付出微小的代价。另外,在大多数状况下,你不须要领有 C 给你的能力来实现 websockets。
同样,应用 Mongoose,你能够限度你的底层 API 拜访的范畴,但能够开释出很多潜在的收益和良好的 DX。
2. 如何连贯 Mongoose + MongoDB
首先,让咱们疾速看看在 2020 年应该如何用 Mongoose 连贯到你的 MongoDB 数据库。
mongoose.connect(DB_CONNECTION_STRING, {
useNewUrlParser: true,
useUnifiedTopology: true,
useCreateIndex: true,
useFindAndModify: false
})
这种连贯格局确保你正在应用 Mongoose 的新 URL 解析器,而且你没有应用任何废除的做法。
3. 如何执行 Mongoose 操作
当初咱们先来疾速讨论一下 Mongoose 的操作,以及你应该如何执行这些操作。
Mongoose 为您提供以下两种抉择:
- 基于游标的 (Cursor-based) 查问
- 全程检索 (Full fetching) 查问
3.1 基于游标的 (Cursor-based) 查问
基于游标的查问是指一次只解决一条记录,同时一次从数据库中获取一条或一批文档。这是一种在无限的内存环境下解决海量数据的无效形式。
设想一下,你要在 1GB/ 1 核云服务器上解析总大小为 10GB 的文档。你不能获取整个汇合,因为那在你的零碎中不适合。游标是一个很好的(也是惟一的)抉择。
3.2 全程检索 (Full fetching) 查问
这是一种查问类型,你能够一次性取得查问的全副响应。在大多数状况下,这就是你要应用的办法。因而,咱们将在这里次要关注这个办法。
4. 如何应用 Mongoose 模型(Models)
模型是 Mongoose 的超级力量,它们帮忙你执行“schema”规定,并为你的 Node 代码提供无缝集成到数据库调用中。
第一步是定义一个好的模型:
import mongoose from 'mongoose'
const CompletedSchema = new mongoose.Schema(
{type: { type: String, enum: ['course', 'classroom'], required: true },
parentslug: {type: String, required: true},
slug: {type: String, required: true},
userid: {type: String, required: true}
},
{collection: 'completed'}
)
CompletedSchema.index({slug: 1, userid: 1}, {unique: true})
const model = mongoose.model('Completed', CompletedSchema)
export default model
这是一个间接从 codedamn 的代码库中删减的例子。这里有一些乏味的事件你应该留神。
- 在所有须要的字段中,尽量放弃
required: true
。如果你在创建对象时没有应用 TypeScript 这样的动态类型查看零碎来帮助你正确地查看属性名,那么这能够为你省去很多麻烦。另外,收费的验证也超级酷。 - 定义索引和惟一字段。
unique
属性也能够在模式中增加。索引是一个很宽泛的话题,所以我在这里就不深究了。但从大范畴来看,它们的确能够帮忙你放慢很多查问速度。 - 明确定义一个汇合名。尽管 Mongoose 能够依据模型的名称主动给出一个汇合名称(例如这里的
Completed
here),但在我看来这太形象了。你至多应该晓得你的数据库名称和代码库中的汇合。 - 如果能够,请应用枚举来限度值。
5. 如何执行 CRUD 操作
CRUD 是指创立、读取、更新和删除。这是四个根本选项,有了这四个选项,你就能够在数据库中进行任何模式的数据操作。让咱们疾速看看这些操作的一些例子。
5.1 Create
这简略来说就是在数据库中创立一条新记录。让咱们应用咱们下面定义的模型来创立一条记录。
try {const res = await CompletedSchema.create(record)
} catch(error) {console.error(error)
// handle the error
}
同样,这里有一些提醒:
- 应用 async-await 而不是回调(看起来不错,但没有突破性的性能劣势)
- 在查问四周应用 try-catch 块,因为你的查问可能会因为一些起因而失败(重复记录、谬误的值等)。
5.2 Read
这意味着从数据库中读取现有的值。这听起来很简略,但你应该晓得 Mongoose 的几个小问题。
const res = await CompletedSchema.find(info).lean()
- 你能看到那里的
lean()
函数调用吗?它对性能超级有用。默认状况下,Mongoose 会解决从数据库中返回的文档,并在其上增加其 神奇 的办法(例如.save
)。 - 当你应用
.lean()
时,Mongoose 会返回一般的 JSON 对象,而不是内存和资源惨重的文档。使查问速度更快,对 CPU 的耗费也更小。 - 然而,如果你的确想要更新数据,你能够省略
.lean()
(咱们接下来会看到)
5.3 Update
如果你曾经有了一个 Mongoose 文档(没有应用 .lean()
触发),你能够简略地去批改对象属性,而后用 object.save()
保留它。
const doc = await CompletedSchema.findOne(info)
doc.slug = 'something-else'
await doc.save()
记住,这里有两个数据库调用。第一个是在 findOne 上
,第二个是在 doc.save
上。
如果能够,你应该总是缩小拜访数据库的申请数量(因为如果比拟内存、网络和磁盘,网络简直总是最慢的)。
在另一种状况下,能够应用如下查问:
const res = await CompletedSchema.updateOne(<condition>, <query>).lean()
并且只会对数据库进行一次调用。
5.4 Delete
应用 Mongoose 删除也很简略,让咱们看看如何删除单个文档:
const res = await CompletedSchema.deleteOne(<condition>)
和 updateOne
一样 deleteOne
也承受第一个参数作为文档的匹配条件。
还有另一个办法叫 deleteMany
,只有当你晓得要删除多个文档时才应用。
在任何其余状况下,总是应用 deleteOne
来防止意外的屡次删除,特地是当你试图本人执行查问时。