教程说明
本文是《面向Node.js开发者的MongoDB教程》的第一章,本教程大部分参考了《MongoDB权威指南(第二版)》,Kirstina Chodorow著(O'Reilly, 2013)
。可权威指南中使用的MongoDB版本是2.4.x的。在实际写用户代码时,发现有些语法已经不可用了,本教程所使用的环境中MongoDB版本为5.0.x的最新版。并结合我自己是一名Node.js开发者,教程中应用内代码都是使用的Node.js + Mongoose编写。
安装MongoDB
windows/Linux/MacOS等各个环境MongoDB的安装大家可以在MongoDB官网-《MongoDB社区下载》自行下载安装。本文我更加倾向于读者尝试使用Docker安装MongoDB,理由有两个:
1. 简单: 只需要使用一行命令,就可以通过镜像运行一个MongoDB数据库的容器
2. 易于调试: 教程中可能需要对同一份数据进行增删改查,使用Docker可以创建多个互不干扰的容器,方便调试对比。
数据来源(B站热点)
本次教程中数据库的数据,来源来自B站的“综合热门”的后端接口数据。笔者提供了25300条文档数据,方便之后的教程中用来进行数据的增删改查。下面取了一条数据库中的数据:
{
"_id" : ObjectId("6123b52a64ceb40e2917c933"),
"videos" : 1,
"tid" : 17,
"desc" : "",
"duration" : 1537,
"aid" : 675061011,
"tname" : "单机游戏",
"copyright" : 1,
"pic" : "http://i1.hdslb.com/bfs/archive/7b9bff6a7e31bc55fc6a80341a23c43225b5f24e.jpg",
"title" : "三界四洲不可救【龙崎黑神话解析03】",
"pubdate" : ISODate("1970-01-19T20:41:24.902Z"),
"ctime" : ISODate("1970-01-19T20:41:24.902Z"),
"state" : 0,
"owner" : {
"mid" : 4564056,
"name" : "龙崎棒棒糖",
"face" : "http://i0.hdslb.com/bfs/face/74b6d0663e92c0595c40033a1f3495aff7d27f4b.jpg"
},
"stat" : {
"aid" : 675061011,
"view" : 1255439,
"danmaku" : 8719,
"reply" : 6384,
"favorite" : 30489,
"coin" : 81238,
"share" : 12405,
"now_rank" : 0,
"his_rank" : 46,
"like" : 99243,
"dislike" : 0
},
"dynamic" : "原本写了8000字文案,结果把自己累出病来了,就删了一千多字,途中电脑崩溃了一次,把写了一个半小时的文案和其他保存文件给搞没了,可以说是我有史以来最敢的一期视频,技术部分感谢沙滩大佬的传授",
"cid" : 395060770,
"dimension" : {
"width" : 1920,
"height" : 1080,
"rotate" : 0
},
"short_link" : "https://b23.tv/BV18U4y1j765",
"short_link_v2" : "https://b23.tv/BV18U4y1j765",
"first_frame" : "http://i0.hdslb.com/bfs/storyff/n210823a22pk2invww0sv02xkt574ew8_firsti.jpg",
"bvid" : "BV18U4y1j765",
"season_type" : 0,
"rcmd_reason" : {
"content" : "百万播放",
"corner_mark" : 0
},
"update_time" : ISODate("2021-08-23T14:48:10.392Z"),
"__v" : 0
}
大家可以通过百度网盘下载我提供的数据文件bilihot.json
,并导入到MongoDB中。
使用mongoimport
导入bilihot.json
:
$ mongoimport -d bilibili_hot -c hotspots --file /bilihot.json --type json
参数说明:
-d
: 数据库名称-c
: 集合名称--file
: 要导入的文件路径--type
: 导入的文件类型,默认为json
建议使用云服务器
对于大部分前端开发者而言,不仅建议在本地跑一遍相关代码,更加建议自己购买一个云服务器,安装MongoDB后使用远程命令行连接运行一遍。这样有利于学习Linux相关命令、适应后端运行环境。
阿里云-云服务器最新优惠活动地址
MongoDB基础
数据库
在MongoDB中,多个文档组成集合,而多个集合可以组成数据库。一个MongoDB实例可以承载多个数据库,每个数据库拥有0个或者多个集合。每个数据库都有独立的权限,即便是在磁盘上,不同的数据库也放置在不同的文件中。
在MongoDB中,我们可以使用use db_name
来切换数据库,例如本教程中我们的数据库名称为bilibili_hot
,那么你可以通过以下命令进入到该数据库。
> use bilibili_hot
文档
文档(document)是MongoDB中的核心概念,如果你熟悉MySQL等关系型数据库,你可以将文档比喻为一行。文档在JavaScript文档被表示为为对象,例如刚才存入数据库中的一条B站热点数据就是一个文档:
{
"title" : "三界四洲不可救【龙崎黑神话解析03】"
"pubdate" : ISODate("1970-01-19T20:41:24.902Z"),
"ctime" : ISODate("1970-01-19T20:41:24.902Z"),
"duration" : 1537,
...
}
MongoDB的文档区分类型和大小写,并且不能有重复的key,这和JavaScrit中对象的概念基本一致,对于Node.js开发者来说很好理解。例如下面两个文档就是不同的:
{
"title" : "三界四洲不可救【龙崎黑神话解析03】"
}
{
"Title" : "三界四洲不可救【龙崎黑神话解析03】"
}
下面两个文档也是不同的:
{
"duration" : 1537
}
{
"duration" : "1537"
}
集合
集合(collection)就是一组文档,一个或多个集合组成了一个MongooDB的数据库,我们可以将集合比喻为关系型数据库中的一张表。
集合的英文单词是collection
,这个单词在MongoDB的操作中经常会使用到:
- 例如显示所有的集合:
> show collections
- 创建集合
statistics
> db.createCollection('statistics')
- 查询
hotspots
集合中的全部文档数据:
> db.getCollection('hotspots').find({})
MongoDB中的集合是动态模式,这意味着集合中的文档的结构和类型可以是各式各样的,下面两个文档是可以放在同一个集合中,一个文档存的是视频的名称,一个文档存的是视频的播放次数。
{ "title": "三界四洲不可救【龙崎黑神话解析03】" },
{ "view": 1280 }
上面的两个文档,不仅是值的类型不一致,同时键也不一致。作为一名有经验的程序员,肯定不会在集合中这样去组织文档。针对上面的的情况,我们建议使用不同的集合去存储视频的基本信息和视频的统计信息(播放数、点赞数、投币数等)
mongod
启动MongoDB
在命令行执行mongod
,即可启动MongoDB服务器,mongod在启动时可以使用许多可配置项,下面列举几个常用的:
$ mongod
--dbpath
: 指定一个目录作为数据目录,其默认目录为/data/db
--port
: 指定服务器监听的端口号,mongod默认占用27017
端口--fork
:调用fork
创建子进程,在后台运行MongoDB。启用--fork
时,必须同时启用--logpath
--logpath
:指定文件作为输出信息的文件,而非在命令行上输出。--config
:额外加载配置文件,未在命令行中指定的选项将使用配置文件中的从参数。
停止MongoDB
关闭运行中的服务器的命令是一个管理员命令,首先我们需要在Mongo Shell中切换到admin
数据库。
> use admin
再使用db.shutdownServer()
> db.shutdownServer()
mongo
在命令行执行mongo
,可以启动mongosh(又称MongoDB Shell),它是我们学习MongoDB最重要的工具,绝大多数的MongoDB操作都需要使用shell完成。在运行mongo
前,请先执行mongod
运行MongoDB服务器,之后shell将自动连接到MongoDB服务器。
$ mongo
MongoDB server version: 5.0.2
---
>
shell是一个独立的MongoDB客户端,启动时,shell会连接到MongoDB服务器的test数据库我们可以使用db
命令查看当前使用了哪个数据库:
- 查看当前数据库
db
> db
test
- 查看目前所有的数据库
show dbs
> show dbs
admin 0.000GB
bilibili_hot 0.015GB
config 0.000GB
local 0.000GB
- 使用某一个数据库
use 数据库名
, 例如使用bilibili_hot
数据库
> use bilibili_hot
switched to db bilibili_hot
CRUD
创建文档
insert()
方法用于把文档插入集合中,我们把一条数据插入到hotspots
集合下
> db.hotspots.insert({
videos: 1,
tid: 17,
desc: "",
duration: 1537,
aid: 675061011,
tname: "单机游戏",
copyright: 1,
pic: "http://i1.hdslb.com/bfs/archive/7b9bff6a7e31bc55fc6a80341a23c43225b5f24e.jpg",
title: "三界四洲不可救【龙崎黑神话解析03】(新增)",
pubdate: new Date("1970-01-19T20:41:24.902Z"),
ctime: new Date("1970-01-19T20:41:24.902Z"),
state: 0,
owner: {
mid: 4564056,
name: "龙崎棒棒糖",
face: "http://i0.hdslb.com/bfs/face/74b6d0663e92c0595c40033a1f3495aff7d27f4b.jpg",
},
stat: {
aid: 675061011,
view: 1255439,
danmaku: 8719,
reply: 6384,
favorite: 30489,
coin: 81238,
share: 12405,
now_rank: 0,
his_rank: 46,
like: 99243,
dislike: 0,
},
dynamic:
"原本写了8000字文案,结果把自己累出病来了,就删了一千多字,途中电脑崩溃了一次,把写了一个半小时的文案和其他保存文件给搞没了,可以说是我有史以来最敢的一期视频,技术部分感谢沙滩大佬的传授",
cid: 395060770,
dimension: {
width: 1920,
height: 1080,
rotate: 0,
},
short_link: "https://b23.tv/BV18U4y1j765",
short_link_v2: "https://b23.tv/BV18U4y1j765",
first_frame:
"http://i0.hdslb.com/bfs/storyff/n210823a22pk2invww0sv02xkt574ew8_firsti.jpg",
bvid: "BV18U4y1j765",
season_type: 0,
rcmd_reason: {
content: "百万播放",
corner_mark: 0,
},
update_time: new Date("2021-08-23T14:48:10.392Z")
})
查询文档
find()
方法用于查询集合中的文档,如果想查询一个文档,可用findOne()
。我们来找到刚才新增的那一个文档:
> db.hotspots.findOne({ title: '三界四洲不可救【龙崎黑神话解析03】(新增)' })
修改文档
update()
方法用来修改文档,我们来改变刚才新增的那个文档的title
字段:
> db.hotspots.update(
{ title: "三界四洲不可救【龙崎黑神话解析03】(新增)" },
{ $set: { title: "三界四洲不可救【龙崎黑神话解析03】(最新新增)" } }
)
删除文档
remove()
方法用来将文档从集合中永久移除,我们来把刚才更改过的那条文档给删除掉:
> db.hotspots.remove({ title: '三界四洲不可救【龙崎黑神话解析03】(最新新增)' })
Mongoose基础
Mongoose是在node环境中MongoDB数据库操作的封装,一个对象模型工具。将数据库中的数据转换为JavaScript对象以方便我们在应用中使用。
安装Mongoose
npm install mongoose --save
Connection
你可以使用mongoose.connect()
方法连接到MongoDB:
const mongoose = require("mongoose");
mongoose
.connect("mongodb://127.0.0.1:27019/bilibili_hot", { useNewUrlParser: true })
.then(() => console.log("MongoDB Connected"))
.catch((err) => console.log(err));
该方法的第1个参数是MongoDB的连接地址,第2个参数是一个options
对象,该对象会被Mongoose传递给MongoDB的驱动程序,你可以在《MongoDB Node.js驱动程序文档》中找到完成的options
的配置项, 下面是一些比较重要的配置项:
user/pass
: 用于认证的用户名和密码。Mongoose 特有,等价于 MongoDB 驱动的auth.user
和auth.password
选项。useFindAndModify
:默认为true
,设置为false
会让findOneAndUpdate()
和findOneAndRemove()
使用findOneAndUpdate()
而不是findAndModify()
。poolSize
:MongoDB 驱动程序将为此连接保持打开的最大套接字数。默认情况下,poolSize
是5。useNewUrlParser
:底层 MongoDB 驱动程序已弃用其当前的连接字符串解析器。因为这是一个重大变化,他们添加了一个useNewUrlParser
标志,允许用户在发现新解析器中的错误时回退到旧解析器。你应该设置,useNewUrlParser: true
。
Schema
Schema是Mongoose中的重要概念,Schema定义了集合中文档的结构。结合上文提到的数据库的数据,我们定义的Schema如下所示:
// HotSpot.js
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const hotSpotSchema = new Schema({
aid: {
type: Number,
required: true,
},
videos: {
type: Number,
default: 1,
},
tid: {
type: Number,
default: 0,
},
tname: {
type: String,
require: true,
},
copyright: {
type: Number,
dafault: 1,
},
pic: {
type: String,
},
title: {
type: String,
require: true,
},
pubdate: {
type: Date,
},
ctime: {
type: Date,
require: true,
},
desc: {
type: String,
default: "",
},
state: {
type: Number,
},
duration: {
type: Number,
default: 0,
},
owner: {
mid: {
type: Number,
},
name: {
type: String,
},
face: {
type: String,
},
},
stat: {
aid: {
type: Number,
},
view: {
type: Number,
},
danmaku: {
type: Number,
},
reply: {
type: Number,
},
favorite: {
type: Number,
},
coin: {
type: Number,
},
share: {
type: Number,
},
now_rank: {
type: Number,
},
his_rank: {
type: Number,
},
like: {
type: Number,
},
dislike: {
type: Number,
},
},
dynamic: {
type: String,
},
cid: {
type: Number,
},
dimension: {
width: {
type: Number,
},
height: {
type: Number,
},
rotate: {
type: Number,
},
},
short_link: {
type: String,
},
short_link_v2: {
type: String,
},
first_frame: {
type: String,
},
bvid: {
type: String,
},
season_type: {
type: Number,
},
rcmd_reason: {
content: {
type: String,
},
corner_mark: {
type: Number,
},
},
update_time: {
type: Date,
},
});
Model
Model
是从Schema
编译来的构造函数,这也就是为什么这里需要使用大写字母开头的原因。Model
负责从MongoDB中创建和读取文档。
当你调用mongoose.model()
是时,Mongoose就会为你编译一个Model
。
const HotSpot = mongoose.model("hotSpot", hotSpotSchema);
- 参数1: 是collection的单数名称,也就是说,当你的数据库中没有任何集合的时候,当你使用Mongoose连接到MongoDB,并准备插入数据时,Mongoose会自动为你创建一个叫
hotspots
的集合。 - 参数2: 调用
new Schema
所创建的Schema
CRUD
创建好Model
后,你就可以使用Model
对应的方法操作集合中的文档了,这里我们简单的介绍一下使用Mongoose中的CRUD方法。
创建文档
你可以使用Model.create()
创建一条文档数据:
const newVideo = {
videos: 1,
tid: 17,
desc: "",
duration: 1537,
aid: 675061011,
tname: "单机游戏",
copyright: 1,
pic: "http://i1.hdslb.com/bfs/archive/7b9bff6a7e31bc55fc6a80341a23c43225b5f24e.jpg",
title: "三界四洲不可救【龙崎黑神话解析03】(新增)",
pubdate: new Date("1970-01-19T20:41:24.902Z"),
ctime: new Date("1970-01-19T20:41:24.902Z"),
state: 0,
owner: {
mid: 4564056,
name: "龙崎棒棒糖",
face: "http://i0.hdslb.com/bfs/face/74b6d0663e92c0595c40033a1f3495aff7d27f4b.jpg",
},
stat: {
aid: 675061011,
view: 1255439,
danmaku: 8719,
reply: 6384,
favorite: 30489,
coin: 81238,
share: 12405,
now_rank: 0,
his_rank: 46,
like: 99243,
dislike: 0,
},
dynamic:
"原本写了8000字文案,结果把自己累出病来了,就删了一千多字,途中电脑崩溃了一次,把写了一个半小时的文案和其他保存文件给搞没了,可以说是我有史以来最敢的一期视频,技术部分感谢沙滩大佬的传授",
cid: 395060770,
dimension: {
width: 1920,
height: 1080,
rotate: 0,
},
short_link: "https://b23.tv/BV18U4y1j765",
short_link_v2: "https://b23.tv/BV18U4y1j765",
first_frame:
"http://i0.hdslb.com/bfs/storyff/n210823a22pk2invww0sv02xkt574ew8_firsti.jpg",
bvid: "BV18U4y1j765",
season_type: 0,
rcmd_reason: {
content: "百万播放",
corner_mark: 0,
},
update_time: new Date("2021-08-23T14:48:10.392Z")
};
HotSpot.create(newVideo, function (err, video) {
if (err) {
console.log(err);
return;
}
console.log(video)
});
查询文档
查询的方法有很多,目前我们只介绍Model.find()
,让我们来查询刚才新增的那一条数据吧:
HotSpot.find({ title: '三界四洲不可救【龙崎黑神话解析03】(新增)'}, function(err, videos) {
if (err) {
console.log(err)
return
}
console.log(videos)
})
修改文档
我们可以使用Model.updateOne()
更新数据库中的文档
HotSpot.updateOne(
{ title: "三界四洲不可救【龙崎黑神话解析03】(新增)" },
{ $set: { title: "三界四洲不可救【龙崎黑神话解析03】(最新新增)" } },
function (err, raw) {
if (err) {
console.log(err);
return;
}
console.log(raw);
}
);
删除文档
让我们使用Model.deleteOne()
删除刚才更改的文档吧,把我们的数据库恢复原貌:
const raw = await HotSpot.deleteOne({ title: "三界四洲不可救【龙崎黑神话解析03】(最新新增)" });
console.log(raw)
Mongoose总结
在想使用Mongoose来对MongoDB进行操作前,需要先进行三步操作,分别是:
- 连接MongoDB
- 生成Schema
- 生成Model
然后在Model中可以进行相应的CRUD操作
const mongoose = require("mongoose");
const hotSpotSchema = new Schema({...}) // 生成Schmea
const HotSpot = mongoose.model("hotSpot", hotSpotSchema); // 生成Model
// 连接MongoDB
mongoose
.connect("mongodb://127.0.0.1:27019/bilibili_hot", { useNewUrlParser: true })
.then(() => console.log("MongoDB Connected"))
.catch((err) => console.log(err));
// 数据库CRUD操作
HotSpot.findOne({ ... })
其他
1. 使用Docker安装MongoDB
运行MongoDB容器 使用下面命令运行一个mongo容器,如果你本地没有找到mongo的镜像,那么docker会自动拉取远程镜像,可能会耗时比较长。
$ docker run -p 27019:27017 --name mymongo -v ~/db:/data/db -d mongo
参数说明:
-p
: 端口映射,-p 27019:27017
是指将容器内27017
端口映射到宿主机的27019
端口--name
:容器命名,--name mymongo
将该运行中的容器命名为mymongo
-v
: 数据卷,-v ~/db:/data/db
将容器内存放数据的/data/db
目录映射到宿主机的~/db
目录上
查看容器运行状态
使用docker ps
命令查看容器的运行状态
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ddfc4fd875e6 mongo "docker-entrypoint.s…" 4 minutes ago Up 4 minutes 0.0.0.0:27019->27017/tcp, :::27019->27017/tcp mymongo
进入容器内并进入bash操作
我们可以使用docker exec
进入容器内,调式MongoDB。需要注意的是,ddfc4fd875e6
是上一步执行docker ps
命令显示的CONTAINER ID
,读者的值肯定和笔者的不一致,需替换成自己的CONTAINER ID
。
$ docker exec -it ddfc4fd875e6 /bin/bash
root@ddfc4fd875e6:/#
上面表示已经进入到了容器内部,我们来查看一下MongoDB的版本,在命令行输入mongo
进入MongoDB shell。
root@ddfc4fd875e6:/# mongo
此时进入到了MongoDB shell中,我们就可以执行相应操作了
# 查看MongoDB版本
> db.version()
5.0.2
# 显示数据库
> show dbs
admin 0.000GB
config 0.000GB
local 0.000GB
退出shell和容器
# 输入exit回车 退出MongoDB shell
> exit
bye
# 输入exit回车,退出容器
root@ddfc4fd875e6:/# exit
exit
2. 参考文献
- Mongoose官网
- MongoDB官网
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!