绑定完请刷新页面
取消
刷新

分享好友

×
取消 复制
Node.js 系列 - 使用 MongoDB 数据库
2020-05-22 11:32:08

# 前言

作为还在漫漫前端学习路上的一位自学者。我以学习分享的方式来整理自己对于知识的理解,同时也希望能够给大家作为一份参考。希望能够和大家共同进步,如有任何纰漏的话,希望大家多多指正。感谢万分!


在这一节让我们开始学着在 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 & 可视化工具

这篇文章我不想过多讲安装步骤, 大家可以自行去查阅方法.


Windows 平台安装 MongoDBwww.runoob.com图标Mac OSX 平台安装 MongoDBwww.runoob.com图标

为了更方便的管理 MongoDB 数据库, 我推荐使用 Studio 3T 可视化工具.

The MongoDB GUI, IDE & Client for Professional Devs - Studio 3Tstudio3t.com图标

使用方法可参照下面链接内容:

MongoDB可视化工具Studio 3T的使用blog.csdn.net图标

# 在 Node.js 中使用 MongoDB

MongoDB Driver API for Node.jsmongodb.github.io

想要在 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 方法去获取目标集合的实例对象. 该方法个参数为集合的名字. 如果用这个名字的集合之前不存在, 它会自动帮你创建.

之后我们用刚刚获得的集合实例的 insertManyinsertOne 方法来向集合插入文档.

区别是: 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'}]}

## 更新文档

使用集合实例的 updateOneupdateMany 方法可以更新文档. 它们的个参数为查询对象, 用来匹配到需要更新的文档. 第二个参数为更新对象, 也就是要应用于文档上的更新内容. 后一个参数为回调函数, 回调函数个参数为错误信息, 第二个为操作结果. 这两个方法的区别是 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();
    });
});

## 删除文档

想要删除一个文档可以用 deleteOnedeleteMany 方法. 参数为查询对象, 后一个参数为回调函数.

比如我们要删除 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 单页面程序zhuanlan.zhihu.com图标

如果喜欢的话就点个关注吧!O(∩_∩)O 谢谢各位的支持❗️

分享好友

分享这个小栈给你的朋友们,一起进步吧。

MongoDB资料专区
创建时间:2020-05-08 13:54:47
MongoDB是一个介于关系数据库和非关系数据库之间的产品。MongoDB是一个基于分布式文件存储 [1] 的数据库。由C++语言编写。旨在为WEB应用提供可扩展的高性能数据存储解决方案。
展开
订阅须知

• 所有用户可根据关注领域订阅专区或所有专区

• 付费订阅:虚拟交易,一经交易不退款;若特殊情况,可3日内客服咨询

• 专区发布评论属默认订阅所评论专区(除付费小栈外)

技术专家

查看更多
  • 小雨滴
    专家
戳我,来吐槽~