使用 MongoDB 与 Deno
了解何时使用 NoSQL 与 MongoDB。
在上一章中,我们讨论了关系型数据库(SQL)的优缺点及其一些应用场景。本章我们将使用 MongoDB 作为数据库,对非关系型数据库(NoSQL)的优缺点进行同样的分析。
NoSQL 数据库的优缺点
| 优点 | 缺点 |
|---|---|
| 易于使用:这可能是一个主观观点,但 NoSQL 数据库(尤其是文档型数据库)对于初学者来说通常更容易搭建和使用。 | 数据不一致:没有固定模式也可能带来问题。如果未在保存前验证必填字段,可能会导致脏数据进入系统。 |
| 灵活性:这可能是上述优点的原因之一。NoSQL 数据库不依赖于固定模式,因此更容易修改数据格式,具备更高的灵活性。 | 索引支持有限:与 SQL 数据库相比,NoSQL 数据库的索引功能较弱,配置起来更复杂。 |
| 可扩展性:MongoDB 的横向扩展能力非常出色,配置简单且能高效完成扩展任务。其他许多 NoSQL 数据库也具有类似特性。 | 数据冗余:在使用文档型数据库时,由于实体之间的关联关系不够明确,容易出现数据重复的情况。 |
应用场景
NoSQL 数据库通常用于电商系统。在这些场景中,销售的商品种类繁多,差异极大,难以设计统一的通用模式;而为每种商品单独定义模式也不现实。
使用 NoSQL 可以解决这一问题:无需担心模式限制,可以灵活存储所需的数据。不过,仍需定义一些通用的必填字段(如 ID、价格等),在大型项目中建议通过父类型和继承机制来组织结构。
在我们的示例中,将使用 NoSQL 来存储书籍、作者和评论信息。
添加数据
我们可以使用 MongoDB Compass 轻松创建集合并添加数据。如果希望使用 Mongo Shell(也可通过 MongoDB Compass 调用),可以执行以下命令:
-
查看可用数据库:
1show databases -
使用指定数据库:
1use educative -
创建集合:
1db.createCollection("books") -
插入一条文档:
1db.books.insertOne({"title":"Eloquent JavaScript, Third Edition","price":32.23,"identifier":"9781593279509","description":"JavaScript lies at the heart of almost every modern web application, from social apps like Twitter to browser-based game frameworks like Phaser and Babylon. Though simple for beginners to pick up and play with, JavaScript is a flexible, complex language that you can use to build full-scale applications.","authorId":"61dad1982e6de6ae05c72165","reviewIds":[]}) -
由于我们使用
db.<集合名>.insertOne,也可以替换为insertMany、updateOne、updateMany、deleteOne、deleteMany等方法。更多细节可查阅 MongoDB 官方文档。
添加依赖
有许多不同的驱动可供选择,本例中我们将使用 denodrivers 提供的驱动。
我们需要导入用于建立连接和执行查询的客户端:
1import { Bson, MongoClient } from "https://deno.land/x/mongo/mod.ts";
我们可以使用 Bson 来定义 ObjectId,这是 MongoDB 中表示 ID 的方式。
建立连接
添加导入语句后,可以根据需要以三种不同方式建立数据库连接:
1const client = new MongoClient(); 2 3// 连接本地数据库 4await client.connect("mongodb://localhost:27017"); 5 6// 连接 Mongo Atlas 数据库 7await client.connect({ 8 db: "<db_name>", 9 tls: true, 10 servers: [ 11 { 12 host: "<db_cluster_url>", 13 port: 27017, 14 }, 15 ], 16 credential: { 17 username: "<username>", 18 password: "<password>", 19 db: "<db_name>", 20 mechanism: "SCRAM-SHA-1", 21 }, 22}); 23 24// 使用 SRV 地址连接 25await client.connect( 26 "mongodb+srv://<username>:<password>@<db_cluster_url>/<db_name>?authMechanism=SCRAM-SHA-1", 27);
让我们分析一下代码中的操作:
- 第 1 行: 创建 MongoDB 客户端。
- 第 4 行: 连接到本地 MongoDB 实例(选项 1)。
- 第 7 至 22 行: 连接到 Mongo Atlas 数据库(选项 2)。
- 第 25 至 27 行: 使用 SRV URL 连接 MongoDB 实例(选项 3)。
执行查询
本节我们将学习如何在 Deno 中访问 MongoDB 集合并运行各种查询。为了简化说明,我们以包含基本字段的书籍集合为例。
我们的数据结构如下:
1const types = gql` 2 type Book { 3 _id: String 4 title: String 5 price: Float 6 identifier: String 7 description: String 8 author: Author 9 reviews: [Review] 10 } 11 12 input BookInput { 13 title: String 14 price: Float 15 identifier: String 16 description: String 17 } 18`
访问集合
为了方便地访问集合并操作数据,我们定义一个名为 BookSchema 的接口,该接口将被 Mongo 客户端使用。
1// 定义数据结构接口 2interface BookSchema { 3 _id: Bson.ObjectId; 4 title: string; 5 price: number; 6 identifier: string; 7 description: string; 8} 9 10// 获取数据库和集合 11const db = client.database("educative"); 12const books = db.collection<BookSchema>("books");
现在我们已成功获取集合,接下来可以执行多种数据操作,如下所述。
插入数据
通过 addBook 变更操作插入新书籍,可以这样实现:
1const resolvers = { 2 //. 3 //. 4 //. 5 Mutation: { 6 addBook: async (_parent: any, { input: { title, price, identifier, description } }: any, _context: any, _info: any) => { 7 console.log(`addBook() title=(${title}), price=(${price}), identifier=(${identifier}) description=(${description})`); 8 await books.insertOne({ title, price, identifier, description }); 9 return { 10 done: true, 11 }; 12 }, 13 } 14}
获取数据
要获取数据库中所有书籍,更新 getAllBooks 查询如下:
1const resolvers = { 2 Query: { 3 getAllBooks: async () => { 4 return await books.find().toArray(); 5 }, 6 } 7}
其他操作
我们还可以使用 Mongo 库执行其他操作:
- 更新: 使用
books.updateOne()和books.updateMany()更新书籍。 - 替换: 使用
books.replaceOne()替换书籍。 - 删除: 使用
books.deleteOne()和books.deleteMany()删除书籍。
注意: 我们将在最终版本的接口中使用其中部分操作。