node.js操作数据库之MongoDB+mongoose篇

前言

node.js 的出现,使得用前端语法(javascript)开发后台服务成为可能,越来越多的前端因此因此接触后端,甚至转向全栈发展。后端开发少不了数据库的操作。MongoDB 是一个基于分布式文件存储的开源数据库系统。本文为大家详细介绍了如何用 node.js + mongoose 玩转 MongoDB 。希望能帮到有需要的人。

由于我用Mac开发,以下所有操作都是在Mac下进行。

一、 环境搭建

安装Node.js

有 node 环境的可以跳过。

nodejs官网提供了 macOS 安装包,直接下载安装即可。现在 nodejs 稳定版已经到了 12.11.1

安装MongoDB

MongoDB 是为现代应用程序开发人员和云时代构建的基于文档的通用分布式数据库。

上个月(9月) macOS 包管理器 Homebrew 宣布移除 MongoDB 。原因是去年10月 MongoDB 宣布将其开源许可证从 GNU AGPLv3 切换到 SSPL(Server Side Public License),以此回应 AWS 等云厂商将 MongoDB 以服务的形式提供给用户而没有回馈社区的行为,MongoDB 希望从软件即服务上获取收入。Homebrew 认为 MongoDB 已经不再属于开源范畴...

言归正传,由于上述原因,我们不能直接使用 brew install mongodb 来安装 MongoDB 了。好在 MongoDB 自己维护了一个定制化的 Homebrew tap。并在 Install MongoDB Community Edition
更新了安装步骤。

Mac下 MongoDB 的最新安装步骤如下:

1. 首先安装 Homebrew

Homebrew 是 macOS 的包管理器。因为 OSX 默认不包含 Homebrew brew 包,所以要先安装,已经安装过的可以跳过。

/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

安装过程会有点长,终端输出信息超过一屏,这里我只截取了头尾两部分。


mg-install-homebrew.jpg
mg-install-homebrew-2.jpg

2. 然后获取下 MongoDB Homebrew Tap

brew tap mongodb/brew
mg-tap-mongodb-brew.jpg

3. 最后安装 MongoDB CE(社区版)

brew install mongodb-community@4.2
mg-brew-install-mongodb.jpg

现在你的 Mac 上就已经安装好 MongoDB 环境了。

安装mongoose

node.js 是可以直接操作 MongoDB 的,但是通过 MongoDB 命令语法直接编写 MongoDB 验证、数据类型转换和业务逻辑模版比较繁琐。所以我们使用了 mongoose。

mongoose 是 MongoDB 的一个对象模型工具,它对 MongoDB 的常用方法进行了封装,让 node.js 操作 MongoDB 更加优雅简洁。

刚才的 node.js 和 MongoDB 都是安装在全局环境,mongoose 则是安装在你的项目下:

cd your-project
npm i -S mongoose
mg-install-mongoose.jpg

现在,你的开发环境就已经全部安装好了。

二、启动MongoDB服务

要操作 MongoDB ,首先要启动它。
有两种方式启动 MongoDB 服务:

1. 在前台运行

mongod --config /usr/local/etc/mongod.conf

前台运行的好处就是,可以查看一些反馈和日志,便于调试。另外如果要关闭服务,只需要在终端按 control + c 键即可。

mg-run-mongodb-in-foreground.jpg

2. 也可以作为 macOS 服务,在后台运行

brew services start mongodb-community@4.2

好处是开机就自动启动,随时可以使用。

mg-run-mongodb-as-a-server.jpg

这种启动方式,如果要关闭服务,可以通过 stop 命令:

brew services stop mongodb-community@4.2
mg-stop-mongodb-service.jpg

现在,你的 MongoDB 数据库已经开启了。

三、操作MongoDB

操作之前先解释一下MongoDB和mongoose里的一些核心概念。

MongoDB

  • MongoDB 中的数据记录是一种 BSON 格式的文件(BSON是一种用二进制描述的JSON文件格式)。
  • MongoDB 将文件存储在集合中,将集合存储在数据库中。
  • MongoDB 的数据库、集合都不用手动创建。
  • 集合collection: 相当于关系型数据库中的表table
  • 文件document: MongoDB 的数据记录单位,相当于关系型数据库中的记录row

mongoose

  • schema: 在 mongoose 中,所有的东西都来源于一个 schema,每个schema 映射了一个 MongoDB 的集合,它定义了这个集合中的文档的骨架。
  • model: 一个文件的构造器,通过编译schema得到,一个model的实例就是一个文件model负责从 MongoDB 数据库中创建和读取文档

更多mongoose概念可以在mongoose guide中查阅。

数据库操作:

1. 使用 mongoose 连接 MongoDB

在项目中创建 connection.js 文件

// connection.js file
const mongoose = require('mongoose');
const conn = mongoose.createConnection(

  // 连接地址,MongoDB 的服务端口为27017
  // dbtest是我要使用的数据库名,当往其中写数据时,MongoDB 会自动创建一个名为dbtest的数据库,不用事先手动创建。
  'mongodb://127.0.0.1:27017/dbtest', 

  // 一些兼容配置,必须加,你不写运行的时候会提示你加。
  {
    useNewUrlParser: true,
    useUnifiedTopology: true
   }
)
conn.on('open', () => {
    console.log('打开 mongodb 连接');
})
conn.on('err', (err) => {
    console.log('err:' + err);
})

运行:

node conection.js
mg-connection.jpg

可以看到打印出“打开 mongodb 连接”,并且运行一直在等待。

这说明现在已经成功连接上 MongoDB 了,接下来可以开始操作数据库了。

为了方便扩展起见,我们先对 connection.js 改造一下,让它作为模块导出,这样就可以在其他地方导入复用了。

// connection.js file
const mongoose = require('mongoose');
const conn = mongoose.createConnection(
  'mongodb://127.0.0.1:27017/dbtest',
  {
    useNewUrlParser: true,
    useUnifiedTopology: true
   }
)
conn.on('open', () => {
    console.log('打开 mongodb 连接');
})
conn.on('err', (err) => {
    console.log('err:' + err);
})

module.exports = conn; //commonJs 语法,导出conn模块。

2. 添加操作

save | create 方法

新建insert.js文件

// insert.js file
let mongoose = require('mongoose');

// 导入连接模块
let connection = require('./connection');

// 创建schema
let StudentSchema = new mongoose.Schema({
   name: String,
   age: Number
})

// 通过connection和schema创建model
let StudentModel = connection.model('Student', StudentSchema);


// 通过实例化model创建文档
let studentDoc = new StudentModel({
    name: 'zhangsan',
    age: 20
})

// 将文档插入到数据库,save方法返回一个Promise对象。
studentDoc.save().then((doc) => {
    console.log(doc)
})

运行:

node insert.js

为了更直观看到操作数据库的结果,推荐大家安装一个数据库可视化工具:Robo3T,下载mac版安装即可。

mg-robo3T-download2.jpg

点击 Robo3T 左上角连接我们的数据库后,可以看到 MongoDB 自动帮我们生成了数据库和集合,并且已经插入了一条记录:

mg-insert.jpg

或者还可以直接通过Model的create方法直接插入数据,返回的也是一个Promise:

StudentModel.create({
    name: 'lisi',
    age: 19
}).then((doc) => {
    console.log(doc)
})

3. 读取操作

find 方法

为更加合理复用代码,我们先把 StudentSchema 和 StudentModel 抽离出来:

新建StudentSchema.js文件

// StudentSchema.js file
const mongoose = require('mongoose');

let StudentSchema = mongoose.Schema({
    name: String,
    age: Number
})

module.exports = StudentSchema;

新建StudentModel.js文件

// StudentModel.js file
const connection = require('./connection');
const StudentSchema = require('./StudentSchema');

let StudentModel = connection.model('Student', StudentSchema);

module.exports = StudentModel;

然后新建query.js文件

// query.js file
const StudentModel = require('./StudentModel');

// 富查询条件,对象格式,键值对,下面为查询 name 为 lisi 的记录
StudentModel.find({name: 'lisi'}).then(doc => {
    console.log(doc);
})

运行

node query.js
mg-guery.jpg

可以看到namelisi的记录被打印了出来。

如果想查询整个集合:

// 不放查询条件即查询所有的记录
StudentModel.find({}).then(doc => {
    console.log(doc);
})
mg-guery-all.jpg

可以看到集合中的所有记录被打印了出来。

4. 更新操作

update|updateOne|updateMany方法

新建update.js文件

// update.js file
const StudentModel = require('./StudentModel');

// update 方法接收2个参数,第一个是查询条件,第二个是修改的值
// 下面把name为lisi的记录,将他的age修改为80
StudentModel.update({name: 'lisi'}, {age: 80}).then(result => {
    console.log(result)
})

进入 Robo3T,可以看到数据被更改,切换到表格模式更加直观:

mg-update2.jpg

不过在终端,提示DeprecationWarning: collection.update is deprecated. Use updateOne, updateMany, or bulkWrite instead.

mg-update.jpg

意思是建议我们使用 updateOneupdateMany或者bulkWrite

update 更新查询到的所有结果,方法已经不提倡使用,已被updateMany替代。
updateOne 如果查询到多条结果,只更新第一条记录。
upateMany 更新查询到的所有结果。
bulkWrite 提供可控执行顺序的批量写操作。

为了代码的健壮性,我们应该根据建议将update方法换成updateMany方法。

另外,终端的输出{ n: 1, nModified: 1, ok: 1 }的意思是:

  • “n: 1”:查询到1条记录。
  • “nModified: 1”:需要修改1条记录。(如果修改值和原始值相同,则需要修改的就是0条)
  • “ok: 1”:修改成功1条。

5. 删除操作

remove|removeOne|removeMany|bulkWrite方法

新建remote.js文件

// remove.js file
const StudentModel = require('./StudentModel');

// delete 方法接收1个参数,就是查询条件
// 下面把name为lisi的记录删除
StudentModel.remove({name:'lisi'}).then((result) => {
    console.log(result);
});

进入 Robo3T,可以看到集合里已经没有name为lisi的记录了:

mg-remove2.jpg

在看终端的输出,跟update类似,也提示建议使用新的方法代替。

mg-remove.jpg

意思是建议我们使用 removeOneremoveMany或者bulkWrite

remove 删除查询到所有结果,方法已经不提倡使用,已被removeMany替代。
removeOne 如果查询到多条结果,只删除第一条记录。
removeMany 删除查询到所有结果。
bulkWrite 提供可控执行顺序的批量写操作。

另外,终端的输出{ n: 1, ok: 1, deletedCount: 1 }的意思跟update的类似,就不累述了。

现在我们已经成功地对 MongoDB 数据库进行了 CRUD(添加、读取、更新、删除)操作。欢呼~

更多高级操作,可以到mongoose API 文档中查阅。

四、总结

梳理一下,主要讲了这些内容:

  1. node.js+MongoDB+mongoose 在Mac下的环境搭建,注意使用最新的 MongoDB 的安装方式。
  2. 在Mac下如何启动和关闭 MongoDB 服务。
  3. 介绍了 MongoDBmongoose 的基本核心概念。
  4. 使用 mongoose 连接以及增删改查 MongoDB 操作。可以使用 Robo3T 来更直观地观察数据库。

前端也能玩转数据库开发。
欢迎交流~

文章源码地址:https://github.com/yc111/mongodb-demo

相关网站:
Homebrew官网
MongoDB官网
monggose官网
Robo3T官网
macOS 包管理器 Homebrew 移除 MongoDB

--
欢迎转载,转载请注明出处:
https://champyin.com/2019/10/10/node.js%E6%93%8D%E4%BD%9C%E6%95%B0%E6%8D%AE%E5%BA%93%E4%B9%8BMongoDB+mongoose%E7%AF%87/

本文同步发表于:
node.js操作数据库之MongoDB+mongoose篇 | 掘金

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,233评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,013评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,030评论 0 241
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,827评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,221评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,542评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,814评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,513评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,225评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,497评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,998评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,342评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,986评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,812评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,560评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,461评论 2 266

推荐阅读更多精彩内容