跳到主要内容

MongoDB

简介

MongoDB 是一个基于分布式文件存储的数据库。由 C++语言编写,旨在为 WEB 应用提供可扩展的高性能数据存储解决方案。

MongoDB 是一个介于关系数据库非关系数据库之间的产品,是非关系数据库当中功能最丰富、最像关系数据库的。

核心概念

  • 数据库(database) 数据库是一个数据仓库,数据库服务下可以创建很多数据库,数据库中可以存放很多集合
  • 集合(collection) 集合类似于 JS 中的数组,在集合中可以存放很多文档
  • 文档(document) 文档是数据库中的最小单位,类似于 JS 中的对象

img

通过 JSON 文件来理解 Mongodb 中的概念

  • 一个 JSON 文件 就像是一个 数据库,一个 Mongodb 服务下可以有 N 个数据库
  • JSON 文件中的 一级属性的数组值 就像是 集合
  • 数组中的对象就像是 文档
  • 对象中的属性有时也称之为 字段

一般情况下,一个项目使用一个数据库,一个集合会存储同一种类型的数据

{
"users": [
{ "id": 1, "name": "张三" },
{ "id": 2, "name": "李四" },
{ "id": 3, "name": "王五" }
],
"books": [
{ "id": 1, "name": "你不知道的JS(上)" },
{ "id": 2, "name": "你不知道的JS(中)" },
{ "id": 3, "name": "你不知道的JS(下)" }
]
}

在 MacOS 中安装 MongoDB

MongoDB 社区免费版下载

手动安装

1、根据自己的系统环境选择下载的版本。将下载好的 MongoDB 安装包解压缩,并将文件夹名改为 mongodb

2、打开 Finder 后按 shift + command + g 输入 /usr/local并进入,将解压后的文件放入 local 目录中

3、配置环境变量

先确认终端用的是 bash(MacOS 默认)还是 zsh。如果是 bash 就执行: open -e .bash_profile 打开配置文件,用source .bash_profile使配置生效。

以 zsh 为例,打开 .zshrc 文件:open ~/.zshrc,如果没有就创建一个:touch .zshrc

添加 PATH 环境变量:

export PATH=${PATH}:/usr/local/mongodb/bin

保存并关闭文件,在终端输入 source ~/.zshrc 使配置生效。

在终端中输入 mongod --version,如果显示了 MongoDB 的版本号,则表示安装成功。

mongod -h 可以显示 MongoDB 的帮助信息。

4、创建日志及数据存放目录:

  1. /usr/local/mongodb下创建datalog两个文件夹,然后在 data 目录中创建一个 db 文件夹,这是 MongoDB 默认保存数据的地方
  2. 给目录读写权限,使用sudo chmod 755 /usr/local/mongodb/data /usr/local/mongodb/log。也可以右键 data 文件夹,显示简介,给配置读写权限,db 文件夹同理

Homebrew 安装

使用 Homebrew 在 macOS 上安装 MongoDB

这种方式还会安装 Shell 和 Database Tools

  1. 更新 Homebrew 的软件包列表:
brew update
  1. 自定义 tap
brew tap mongodb/brew
  1. 安装 mongodb,版本号可以自行选择
brew install mongodb-community@8.0

MongoDB Shell

MongoDB Shell 文档

mongosh表示 MongoDB Shell,它提供了命令行界面,用于与 MongoDB 数据库进行交互。它不是必需的,可用图形化工具来更方便的操作数据库。

1、安装方式:

  1. 从压缩包文件安装:MongoDB Shell Download
  2. macOS 推荐使用 Homebrew 安装:
brew install mongosh

2、通过默认端口连接到本地部署:

mongosh
备注

mongod 是启动服务器,mongosh是从命令行连接服务器

启动关闭服务

进入 /usr/local/mongodb/bin目录,可以看到有三个脚本文件:

  • mongod:是 MongoDB 的主节点守护进程。它处理数据请求、管理数据访问权限并执行后台管理操作
  • mongos:MongoDB 集群代理,它负责将客户端请求分派到集群中的多个服务器上
  • install_compass:MongoDB Compass 的安装脚本,它将下载并安装 Compass

三者的文档可以参考官方文档:MongoDB 包组件

启动 MongoDB 服务

如果已经将 bin 目录添加到 PATH 环境变量,则可以直接执行以下命令来启动 MongoDB 服务:

mongod --dbpath /usr/local/mongodb/data/db --logpath /usr/local/mongodb/log/mongod.log --logappend

打开 127.0.0.1:27017 查看,如果显示如下消息则表示启动成功了:

It looks like you are trying to access MongoDB over HTTP on the native driver port.

也可以在启动命令的尾部添加 --fork 参数,它会将 mongod 进程作为守护进程运行,并将其输出写入日志文件。这样会让 MongoDB 在后台运行,在终端中不会显示任何输出。但是在关闭服务时就麻烦一些,不能简单地使用 ctrl c 关闭服务。

注意

MongoDB 8.0 使用 mongosh 作为默认的客户端工具,而不是 mongo

旧版本是在启动服务之后可以打开一个新的 terminal,输入 mongo 连接到 MongoDB 数据库。

可以用可视化工具 Compass 查看数据库。

关闭 MongoDB 服务

停止 mongod 进程

1、通过 mongosh 关闭,如果没有则需要先安装

use admin
db.shutdownServer()

2、如果在启动时没有设置--fork,可以使用 ctrl c 关闭服务

3、使用 kill 命令终止进程

查找 mongod 的进程 ID:

ps aux | grep mongod

类似于如下的输出:

zgh              94069   0.3  0.2 411974096  73136   ??  S     5:26PM   0:13.62 mongod --dbpath /usr/local/mongodb/data/db --logpath /usr/local/mongodb/log/mongod.log --logappend --fork

其中,94069 是 MongoDB 服务的进程 ID(PID)

-2 信号表示 SIGINT,会触发 MongoDB 的正常关闭流程,确保数据写入并安全退出。

kill -2 94069

4、验证是否成功关闭:

  1. 查看127.0.0.1:27017网页打不开
  2. 查看进程,没有 mongod 进程了
  3. 查看日志可以看到 Shut down

图形化管理工具

MongoDB Compass 文档

在首次打开 Compass 时,需要允许 MacOS 允许打开它。打开设置,选择「Privacy & Security」在 Security 中点击「Open Anyway」按钮。

先启动服务,然后添加数据库连接,就可以操作数据库了。

  • 添加连接:Add new connection
  • 断开连接:Disconnect

成功连接数据库后,可以在 Compass 中打开 MongoDB Shell 面板

MongoDB Atlas

MongoDB Atlas是云数据库服务,免费集群有 512MB 的存储空间。

创建集群:

选择 AWS 服务器,就近选择地址

1、Set up connection security

只有添加到访问列表中的 IP 地址才能连接到项目的集群。可以在 Network Access 中添加更多内容。

2、Choose a connection method

Connect to your application 点击 Drivers,进入后无需改动直接复制 URI

3、在本地项目里添加一个 .env文件,写入:MONGODB_URL="你复制的地址"。将地址里的<db_password>整体改成自己的数据库密码。

4、在项目中使用 mongoose 连接数据库。如果启动时报错说你的 IP 地址不在白名单里,可能是 VPN 的问题,即网页走了代理,但是没开全局代理导致本地项目运行时 IP 不匹配,关闭代理即可。

命令行交互

数据库命令

# 显示所有的数据库
show dbs

# 切换到指定的数据库,如果数据库不存在会自动创建数据库
use '数据库名'

# 显示当前所在的数据库
db

# 删除当前数据库
use '库名'
db.dropDatabase()

集合命令

# 创建集合
db.createCollection('集合名称')

# 显示当前数据库中的所有集合
show collections

# 删除某个集合
db.'集合名'.drop()

# 重命名集合
db.'集合名'.renameCollection('newName')

文档命令

# 插入文档
db.'集合名'.insert('文档对象')

# 查询文档
# _id 是 mongodb 自动生成的唯一编号,用来唯一标识文档
db.'集合名'.find('查询条件')

# 更新文档
db.'集合名'.update('查询条件', '新的文档')
db.'集合名'.update({ name: '张三' }, { $set: { age:19 } })

# 删除文档
db.'集合名'.remove('查询条件')

Mongoose

Mongoose 是一个对象文档模型库,方便使用代码操作 mongodb 数据库

使用方式

// 安装、导入 mongoose
const mongoose = require('mongoose');

// 连接数据库,数据库名称是 mydb
mongoose.connect('mongodb://127.0.0.1:27017/mydb');

// 连接成功
mongoose.connection.on('open', () => {
console.log('连接成功');
// 设置集合中文档的属性以及属性值的类型
let BookSchema = new mongoose.Schema({ title: String, author: String, price: Number });

// 创建文档模型对象
let BookModel = mongoose.model('book', BookSchema);

// 插入文档
BookModel.create({ title: '一气化三清', author: '老子', price: 100 }, (err, data) => {
if (err) throw err;
console.log(data);
// 断开连接
mongoose.disconnect();
});
});

// 连接出错
mongoose.connection.on('error', () => {
console.log('连接出错');
});

// 连接关闭
mongoose.connection.on('close', () => {
console.log('连接关闭');
});

文档字段类型

文档结构可选的常用字段类型列表

类型描述
String字符串
Number数字
Boolean布尔值
Array数组,也可以使用 [] 来标识
Date日期
BufferBuffer 对象
Mixed任意类型,需要使用 mongoose.Schema.Types.Mixed 指定
ObjectId对象 ID,需要使用 mongoose.Schema.Types.ObjectId 指定
Decimal128高精度数字,需要使用 mongoose.Schema.Types.Decimal128 指定

字段值验证

Mongoose 有一些内建验证器,可以对字段值进行验证

// 必填项
title: { type: String, required: true }

// 默认值
author: { type: String, default: '匿名' }

// 枚举值,设置的值必须是数组中的
gender: { type: String, enum: ['男','女'] }

// 唯一值
username: { type: String, unique: true }

CURD

数据库的基本操作包括四个,增加(create),删除(delete),修改(update),查(read)

增加

插入一条

SongModel.create({ title: '给我一首歌的时间', author: 'Jay' }, (err, data) => {
if (err) throw err;
console.log(data); // 插入后的数据对象
});

批量插入

const mongoose = require('mongoose');
mongoose.connect('mongodb://127.0.0.1:27017/mydb');

mongoose.connection.on('open', () => {
// 声明文档结构
const PhoneSchema = new mongoose.Schema({ brand: String, color: String, price: Number, tags: Array });
// 创建模型对象
const PhoneModel = mongoose.model('phone', PhoneSchema);
const phoneData = [
{ brand: 'iphone', color: 'dark purple', price: 8999 },
{ brand: 'xiaomi', color: 'red', price: 3499 }
];
PhoneModel.insertMany(phoneData, (err, data) => {
if (err) throw err;
console.log('写入成功');
mongoose.connection.close();
});
});

删除

删除一条数据

SongModel.deleteOne({ _id: '5dd65f32be6401035cb5b1ed' }, err => {
if (err) throw err;
console.log('删除成功');
mongoose.connection.close();
});

批量删除

SongModel.deleteMany({ author: 'Jay' }, err => {
if (err) throw err;
console.log('删除成功');
mongoose.connection.close();
});

更新

更新一条数据

SongModel.updateOne({ author: 'zhangsan' }, { author: '张三' }, err => {
if (err) throw err;
mongoose.connection.close();
});

批量更新数据

SongModel.updateMany({ author: 'lisi' }, { author: '李四' }, err => {
if (err) throw err;
mongoose.connection.close();
});

查询

查询一条数据

SongModel.findOne({ author: 'Jay' }, (err, data) => {
if (err) throw err;
console.log(data);
mongoose.connection.close();
});

// 根据 id 查询数据
SongModel.findById('5dd662b5381fc316b44ce167', (err, data) => {
if (err) throw err;
console.log(data);
mongoose.connection.close();
});

批量查询数据

// 不加条件查询
SongModel.find((err, data) => {
if (err) throw err;
console.log(data);
mongoose.connection.close();
});

// 加条件查询
SongModel.find({ author: 'Jay' }, (err, data) => {
if (err) throw err;
console.log(data);
mongoose.connection.close();
});

条件控制

运算符 在 mongodb 不能使用 > 、<、 >=、 <=、 !== 等运算符,需要使用替代符号

运算符替代符号
>$gt
<$lt
=$gte
<=$lte
!==$ne
// id 号比 3 大的所有的记录
db.students.find({ id: { $gt: 3 } });

// 价格小于 20 的图书
BookModel.find({ price: { $lt: 20 } }, (err, data) => {
if (err) throw err;
console.log(data);
});

逻辑运算

$or 逻辑或

db.students.find({ $or: [{ age: 18 }, { age: 24 }] });

BookModel.find({ $or: [{ author: '张三' }, { author: '李四' }] }, (err, data) => {
if (err) throw err;
console.log(data);
});

$and 逻辑与

db.students.find({ $and: [{ age: { $lt: 20 } }, { age: { $gt: 15 } }] });

// 价格大于 30 且 小于 100
BookModel.find({ $and: [{ price: { $gt: 10 } }, { price: { $lt: 100 } }] }, (err, data) => {
if (err) throw err;
console.log(data);
});

正则匹配

条件中可以直接使用 JS 的正则语法,通过正则可以进行模糊查询

db.students.find({ name: /san/ });

BookModel.find({ name: // }, (err, data) => {
if (err) throw err;
console.log(data);
});

BookModel.find({ name: new RegExp('三') }, (err, data) => {
if (err) throw err;
console.log(data);
});

个性化读取

字段筛选

// 0: 不要的字段
// 1: 要的字段
SongModel.find()
.select({ _id: 0, title: 1 })
.exec((err, data) => {
if (err) throw err;
console.log(data);
mongoose.connection.close();
});

数据排序

// sort 排序
// 1: 升序
// -1: 倒序
SongModel.find()
.sort({ hot: 1 })
.exec((err, data) => {
if (err) throw err;
console.log(data);
mongoose.connection.close();
});

数据截取

// skip 跳过, limit 限定
SongModel.find()
.skip(10)
.limit(10)
.exec((err, data) => {
if (err) throw err;
console.log(data);
mongoose.connection.close();
});

原子操作

$set

用来指定一个键并更新键值,若键不存在则创建。

const { title, content, contentText, category } = req.body;
const data = await article.findById(id);
const updateData = await data.update({
$set: { title, content, contentText, category }
});

$inc

对文档的某个值为数字型(只能为满足要求的数字)的键进行增减。

const data = await article.findOneAndUpdate(
{ _id: id },
{
$inc: { looknums: 1 }
// 阅读量每次增加1
// 若需要减少1,可写成 $inc: {looknums: -1}
}
);
res.json({ code: 200, data: data, msg: '阅读量修改成功' });

$unset

用来删除一个键

$push

{
$push: {
field: value;
}
}

把 value 追加到 field 里面去,field 一定要是数组类型才行,如果 field 不存在,会新增一个数组类型加进去。

$pushAll

同$push,只是一次可以追加多个值到一个数组字段内。

$pull

从数组 field 内删除一个等于 value 值。

$addToSet

增加一个值到数组内,而且只有当这个值不在数组内才增加。

$pop

删除数组的第一个或最后一个元素

$rename

修改字段名称

FAQ

端口占用

报错信息:

 Unclean full-time diagnostic data capture shutdown detected, found interim file, some metrics may have been lost. OK

解决方法:找到项目存放 data 的地方,删除diagnostic.data文件夹即可