# 前言
作为还在漫漫前端学习路上的一位自学者。我以学习分享的方式来整理自己对于知识的理解,同时也希望能够给大家作为一份参考。希望能够和大家共同进步,如有任何纰漏的话,希望大家多多指正。感谢万分!
在这一节让我们开始学着在 Node.js 中使用 MongoDB 数据库.
# 什么是 MongoDB
『 MongoDB 』 是由 C++ 语言编写的, 基于分布式文件存储的数据库. 属于 NoSql (非关系型数据库) 的一种, 旨在为 WEB 应用提供可扩展的高性能数据存储解决方案.
MongoDB 将数据存储为一个文档,数据结构由键值对 key:value
组成. MongoDB 文档类似于 JSON 对象. 字段值可以包含其他文档,数组及文档数组.
下面就是一个 MongoDB 文档 (document) 示例:
{
"name": "Garrik",
"age": 21,
"gender": "Male",
"job": "Frontend Developer"
}
## 关系型数据库 & 非关系型数据库
前面说到 MongoDB 是属于非关系型数据库的一种, 那么什么是关系型? 什么是非关系型?
『 关系型数据库 』以 "行" 和 "列" 组成的 "二维表格" 形式来存储数据, 二维表格简称为称为表, 表又组成了数据库. 之所以称之为关系型数据库, 因为表与表之间采用了现实世界中实体 (entitiy) 与实体 (entitiy) 之间的关系模型. 表中存储格式化结构的数据,每个 "元组"(可以理解为二维表中的一行)的字段组成都是一样的. 即使不是每个元组都需要所有的字段,但数据库会为每个元组都分配所有的字段,这样的结构可以便于表与表之间进行连接等操作. 比较常见的关系型数据库有 Oracle, MySQL 等.
『 非关系数据库 』以 "键值对" 存储,它的结构不固定,每一个元组可以有不一样的字段,可以根据需要增加或减少一些自己的键值对,不会局限于固定的结构. 数据之间的关联性不强制, 使用起来要比关系型数据库更加灵活, 性能也有提升. 比较常见的非关系型数据库有 MongoDB, Redis 等
# MongoDB 基础概念
在具体讲如何使用 MongoDB 之前, 我要先介绍一些基本概念.
## 文档 (document)
『 文档 』是一组 "键值对" 的有序集合. 是 MongoDB 的核心概念. 文档的数据结构被称为 BSON, 是一种类似于 JSON 的二进制形式的存储格式. 下面是一个文档示例:
{
"name": "Garrik",
"age": 21
}
"name"
和 "age"
是这个文档的 "键" (key), "Garrik"
和 21
是 "值" (value)
文档中的值可以是多种不同的数据类型. 在一个集合中各个文档不需要设置相同的字段,并且相同的字段不需要相同的数据类型.
MongoDB 区分类型和大小写. 例如,下面的两个文档是不同的:
{"foo" : 3}
{"Foo" : "3"}
在给文档的 "键" 命名时还需要注意: 文档的键必须是字符串 键不能含有 \0
(空字符)。这个字符用来表示键的结尾 .
和 $
有特别的意义,只有在特定环境下才能使用 以下划线 _
开头的键是保留的 (不是严格要求的) * 一个文档内不能有重复的键
## 集合 (collection)
『 集合 』就是一组文档,如果将 MongoDB 中的一个文档比喻为关系型数据库中的一行,那么一个集合就相当于一张表. 集合存在于数据库中,当个文档插入时,集合就会被创建. 集合没有固定的结构,一个集合里面的文档可以是各式各样的,这被称为 『 动态模式 』
下面是一个集合示例:
{
"site": "www.baidu.com",
"name": "百度"
}
{
"site": "www.google.com",
"name": "Google",
"query": {
"search": "前端开发"
}
}
{
"site": "www.zhihu.com",
"name": "知乎",
"username": "罐装汽水_Garrik"
}
虽然 MongoDB 对集合中的文档类型没有要求, 但通常情况下我们插入集合的数据都会有一定的关联性. 这样会降低查询特定文档的复杂度, 并且提升查询速度.
集合命名时需要注意: 集合名不能是空字符串。 集合名不能含有 \0
(空字符),这个字符表示集合名的结尾。 集合名不能以 system.
开头,这是为系统集合保留的前缀。 用户创建的集合名字不能含有保留字符 $
。
## 数据库 (database)
『 数据库 』由多个集合组成. 在 MongoDB 中可以建立多个数据库。每个数据库都有独立的权限,即便是在磁盘上,不同的数据库也放置在不同的文件中。按照经验,我们将有关一个应用程序的所有数据都存储在同一个数据库中。要想在同一个 MongoDB 服务器上存放多个应用程序或者用户的数据,就需要使用不同的数据库。
MongoDB 的默认数据库为 "db"
,该数据库存储在 data
目录中。
数据库名可以是满足以下条件的任意 UTF-8 字符串: 不是空字符串 不得含有 空格
、.
、$
、/
、\
和 \0
(空字符) 数据库名区分大小写, 推荐全部小写 多 64 字节
数据库终会变成文件系统里的文件,而数据库名就是相应的文件名
文档, 表单, 数据库之间的关系如下图:
# 安装 MongoDB & 可视化工具
这篇文章我不想过多讲安装步骤, 大家可以自行去查阅方法.
为了更方便的管理 MongoDB 数据库, 我推荐使用 Studio 3T 可视化工具.
The MongoDB GUI, IDE & Client for Professional Devs - Studio 3T使用方法可参照下面链接内容:
MongoDB可视化工具Studio 3T的使用# 在 Node.js 中使用 MongoDB
MongoDB Driver API for Node.js想要在 Node.js 中使用 MongoDB, 要先安装 mongodb 模块, 具体步骤不赘述. 在 Node.js 中使用之前, 还需要先在命令行中用 mongod
指令运行 MongoDB 服务器.
以下代码适用于 mongodb 模块 3.x 版本
## 连接数据库
要想连接 MongoDB 数据库, 我们要先创建一个 MongoDB 客户端并且连接上 MongoDB 服务器. 之后在此连接之上创建数据库实例.
// 引入模块
const MongoClient = require('mongodb').MongoClient;
// MongoDB 服务器的地址
const url = 'mongodb://localhost:27017';
// 目标数据库的名字
const dbname = 'mydb';
// 创建 MongoDB 客户端
const client = new MongoClient(url);
// 让客户端连接上服务器
client.connect(function(err) {
if (err) throw err;
console.log("成功连接到 MongoDB 服务器!");
// 创建数据库实例
const db = client.db(dbname);
// 断开连接
client.close();
});
## 插入文档
要想向一个特定的集合插入文档, 首先我们用 db.collection
方法去获取目标集合的实例对象. 该方法个参数为集合的名字. 如果用这个名字的集合之前不存在, 它会自动帮你创建.
之后我们用刚刚获得的集合实例的 insertMany
或 insertOne
方法来向集合插入文档.
区别是: insertOne
用以插入单个文档, 个参数为文档对象 insertMany
用以插入多个文档, 个参数为由文档对象组成的数组
这两个方法的后一个参数都为回调函数. 回调函数的个参数为错误信息, 第二个为处理结果
假如说我们现在要向 usersInfo
集合插入用户信息, 代码如下:
client.connect(function(err) {
if (err) throw err;
const db = client.db(dbname);
// 获取 'usersInfo' 集合的实例对象.
const collection = db.collection('usersInfo');
// 插入单个用户的信息
collection.insertOne({
name: "Garrik",
date_of_birth: new Date("1997-06-04")
}, function(err, result) {
if (err) throw err;
client.close();
})
});
## 查询全部文档
如果我们想查询一个集合下的全部文档, 我们可以使用集合实例的 find
方法. 该方法的个参数为查询条件对象, 如果传入一个空对象 {}
则表示查询全部文档.
client.connect(function(err) {
if (err) throw err;
const db = client.db(dbname);
// 获取 'usersInfo' 集合的实例对象.
const collection = db.collection('usersInfo');
// 查询集合下的所有文档, 然后用 toArray 转换成数组
collection.find({}).toArray(function(err, result) {
if (err) throw err;
console.log(result);
client.close();
});
});
## 查询特定文档
前面说集合实例的 find
方法的个参数为查询条件对象. 想要查找特定文档的前提是知道怎么写查询条件对象.
首先, 平时常用的查询是找到有特定 "键值对" 的文档. 比如找到 "文档中有 name
键, 且它的值为 小明
". 这个查询写成查询条件对象是: {'name':'小明'}
. 假如要查询的文档在 usersInfo
集合下, 在 Node.js 的代码就为:
// 客户端连接上服务器
client.connect(function(err) {
if (err) throw err;
console.log("成功连接到 MongoDB 服务器!");
// 创建数据库实例
const db = client.db(dbname);
//查询 'name' 等于 '小明' 的文档
db.collection('usersInfo').find({ 'name': '小明' }).toArray((err, array) => {
if (err) throw err;
// 打印出匹配到的文档
console.log(array);
// 关闭客户端
client.close();
})
});
如果再想加一条 gender
键的值 等于 male
. 那查询条件对象就为 {'name':'小明', 'gender':'male'}
查询条件对象中各个条件是 AND 的关系. 也就是只匹配 name
键的值等于 小明
, 且 gender
键的值为 male
的文档.
假如集合中的文档有如下:
{ "_id" : ObjectId("5bd1e7df8a5c0550f4722b01"), "name" : "小明", "gender" : "male" }
{ "_id" : ObjectId("5bd1e7e28a5c0550f4722b02"), "name" : "小明", "gender" : "female" }
{ "_id" : ObjectId("5bd1e7e88a5c0550f4722b03"), "name" : "小红", "gender" : "female" }
{ "_id" : ObjectId("5bd1e7f48a5c0550f4722b04"), "name" : "小王", "gender" : "male" }
那么执行上面代码匹配到的为:
{ "_id" : ObjectId("5bd1e7df8a5c0550f4722b01"), "name" : "小明", "gender" : "male" }
## 数值比较
上面的查询条件都是匹配, 也就是匹配文档中某个键的值是否等于多少. 平时常用的另一个查询是范围比较. 也就是 <
, >
, <=
, >=
, !=
. 它们对应的 MongoDB 关键字为 $lt
, $gt
, $lte
, $gte
.
比如要匹配 age
键的值小于 60, 那么查询条件对象为 {'age': {$lt:60}}
.
如果在小于 60 的同时还要大于 18, 那就是 {'age': {$lt:60, $gt:18}}
.
## OR 查询
MongoDB 中有两种方式进行 OR 查询: $in
关键字可以查询一个键的多个值 $or
可以用于多个键值对.
比如我们要查 name
键的值为 小明
或者 小红
的文档. 用 $in
的话, 文档查询对象为 {'name': {$in: ['小明', '小红']}}
; 用 $or
的话, 文档查询对象为 {$or:[{'name':'小明'}, {'name':'小红'}]}
但如果我们要查 name
键的值为 小明
或 小红
, 或者 gender
键的值为 male
的文档的话, $in
和 $or
结合使用会很方便. 其文档查询对象为 {$or:[{'name':{$in:['小明','小红']}}, {'gender':'male'}]}
## 更新文档
使用集合实例的 updateOne
和 updateMany
方法可以更新文档. 它们的个参数为查询对象, 用来匹配到需要更新的文档. 第二个参数为更新对象, 也就是要应用于文档上的更新内容. 后一个参数为回调函数, 回调函数个参数为错误信息, 第二个为操作结果. 这两个方法的区别是 updateOne
只更新匹配到的个文档, updateMany
更新所有匹配到的文档.
比如我们要更新 name
键的值为 小明
的文档的 age
键为 22. Node.js 代码如下: (注意更新对象里面的 $set
关键字)
// 客户端连接上服务器
client.connect(function(err) {
if (err) throw err;
console.log("成功连接到 MongoDB 服务器!");
// 创建数据库实例
const db = client.db(dbname);
//查询 'name' 等于 '小明' 的文档, 然后更新 `age` 键
db.collection('usersInfo').updateOne({ 'name': '小明' }, { $set: { 'age': 22 } }, function(err, result) {
if (err) throw err;
console.log(result);
// 关闭客户端
client.close();
});
});
## 删除文档
想要删除一个文档可以用 deleteOne
和 deleteMany
方法. 参数为查询对象, 后一个参数为回调函数.
比如我们要删除 name
为 小明
的个文档. Node.js 代码如下:
// 客户端连接上服务器
client.connect(function(err) {
if (err) throw err;
console.log("成功连接到 MongoDB 服务器!");
// 创建数据库实例
const db = client.db(dbname);
//删除 'name' 等于 '小明' 的条文档
db.collection('usersInfo').deleteOne({ 'name': '小明' }, function(err, result) {
if (err) throw err;
console.log(result);
// 关闭客户端
client.close();
});
});
好啦,今天的分享就告一段落啦。在下一期我会手把手演示如何实现一个连接了 MongoDB 数据库的 Todo List 页面程序.
罐装汽水Garrik:Node.js 系列 - [实战] TODO 单页面程序如果喜欢的话就点个关注吧!O(∩_∩)O 谢谢各位的支持❗️