第十章:MongoDB

MongoDB

MongoDB

一、简介

1.1 MongoDB 是什么

MongoDB 是一个基于分布式文件存储的数据库(官方地址

1.2 数据库是什么

数据库(DataBase)是按照数据结构来组织、存储和管理数据的应用程序

1.3 数据库的作用

数据库的主要作用就是 管理数据,对数据进行 增(c)、删(d)、改(u)、查(r)

1.4 数据库管理数据的特点

相比于纯文件管理数据,数据库管理数据有如下特点:

  1. 速度更快
  2. 扩展性更强
  3. 安全性更强

1.5 为什么选择 MongoDB

操作语法与 JavaScript 类似,容易上手,学习成本低

二、核心概念

MongoDB 中有三个重要概念需要掌握:

  • 数据库(DataBase):数据库是一个数据仓库,数据仓服务下可以创建很多数据库,数据库中可以存放很多集合
  • 集合(Collection):集合类似于 JS 中的数组,在集合中可以存放很多文档(这个集合就相当于数据表
  • 文档(Document):文档是数据库中的最小单位,类似于 JS 中的对象(这个文档就相当于数据表中的字段

MongoDB核心概念

JSON 文件示例:(用于理解上面的概念)

{	// 一个 json 文件就代表一个数据库
  "accounts": [		// 代表一个集合(即数据表)
    {	// 每一个对象就是一个文档(即数据表中的字段)
      "id": "7JivnCf3M",
      "title": "买复习材料",
      "time": "2024-03-25",
      "type": "-1",
      "account": "89",
      "remarks": "买书了,就一定要去看啊!"
    },
    {
      "id": "4ZqPLTsJB",
      "title": "发工资",
      "time": "2024-03-23",
      "type": "1",
      "account": "90000",
      "remarks": "终于发工资了!"
    },
    {
      "id": "l3nKzkhw7",
      "title": "报班",
      "time": "2024-03-14",
      "type": "-1",
      "account": "10000",
      "remarks": "迫不得已,不得不去"
    },
    {
      "id": "BaA0bRfh8",
      "title": "买水果",
      "time": "2024-03-01",
      "type": "-1",
      "account": "20",
      "remarks": "好久没吃水果了,今天吃一吃"
    }
  ],
  "users": [	// 代表一个集合(即数据表)
      {   // 每一个对象就是一个文档(即数据表中的字段)
          "id": 1,
          "name": "zhangsan",
          "age": 18
      },
      {
          "id": 2,
          "name": "lisi",
          "age": 20
      },
      {
          "id": 3,
          "name": "wangwu",
          "age": 22
      }
  ]
}

我们可以通过 JSON 文件来理解 MongoDB 中的概念:

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

一般情况下:

  • 一个项目使用一个数据库
  • 一个集合会存储同一类型的数据

三、下载安装与启动

下载地址:MongoDB 官网

建议选择 zip 类型,通用性更强。因为选择下载的是 .zip 文件,所以直接解压,一步到位

解压后文件里面的目录结构如下:

MongoDB目录结构

(1)配置步骤如下:

  1. 选择任一磁盘创建空文件夹(不要使用中文路径),将解压之后的文件夹内容剪切进去(我选择的是D盘

  2. 手动创建 data 和 log 两个文件夹:

    创建data和log

  3. 配置环境变量(打开“开始”搜索“环境变量”即可

    找到 Path,进行编辑,将自己的 MongoDB 的 bin 文件地址添加上去

    MongoDB配置环境变量

(2)启动服务:

  1. 创建 db 文件夹(在 data 目录下,创建 db 文件夹)

    因为启动 MongoDB 服务之前必需创建数据量文件的存放文件夹,否则命令不会自动创建,而且不能确定成功。

    创建db文件夹

  2. 在 bin 目录上直接输入 cmd

    bin目录下使用cmd

  3. 输入命令

    输入如下命令后按回车,后面的路径是 data 文件夹下的 db 目录路径(类似 D:\MongoDB\data\db)

    mongod --dbpath D:\MongoDB\data\db
    

    输入mongod命令

    回车后会看到一大堆下面这样的(找到 waiting for connections,则表明服务 已经启动成功

    启动服务

  4. 成功

    在浏览器中输入下面的地址:

    http://localhost:27017/
    

    若显示结果如下,就说明安装成功并结束,也代表服务已经启动成功

    在这里插入图片描述

  5. 使用 mongo 连接本地的 MongoDB 服务

    连接MongoDB服务

注意:

  • 为了方便后续使用 mongod 或 mongo 命令,可以将 bin 目录配置到环境变量 Path 中
  • 千万不要选中服务端窗口的内容,选中后会停止服务,可以通过 敲回车 取消选中来解决

四、命令行交互

命令行交互一般是学习数据库的第一步,不过这些命令在后续用的比较少,所以我们只需要了解即可

4.1 数据库命令

  • 显示所有的数据库

    show dbs
    

    显示所有的数据库

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

    use 数据库名
    

    切换指定数据库

  • 显示当前所在的数据库

    db
    

    显示当前所在的数据库

  • 删除当前数据库

    use 库名
    db.dropDatabase()
    

    删除当前数据库

4.2 集合命令

  • 创建集合

    db.createCollection('集合名称')
    

    创建集合

  • 显示当前数据库中的所有集合

    show collections
    

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

  • 删除某个集合

    db.集合名.drop()
    

    删除某个集合

  • 重命名集合

    db.集合名.renameCollection('newName')
    

    重命名集合

4.3 文档命令

  • 插入文档

    db.集合名.insert(文档对象)
    

    插入文档

  • 查询文档

    db.集合名.find(查询条件)
    // 不加查询条件的话,得到的是所有的文档
    

    _id 是 MongoDB 自动生成的唯一编号,用来唯一标识文档

    查询文档

  • 更新文档

    // 这个方法相当于重写,即用“新的文档”覆盖掉“旧的文档”来实现更新文档的功能,从而导致文档中不需要更新的内容发生丢失
    db.集合名.update(查询条件, 新的文档)
    // 例如:更新 name 为‘张三’的 age = 35	--> 最终得到的结果虽然更新了age,但是其name属性丢失了
    dp.users.update({name: '张三'}, {age: 35})
    
    // 这个方法可以实现更新文档中的某一个数据,并且不会发生数据丢失的情况
    db.集合名.update(查询条件, {$set:要更新的文档})
    // 例如:
    db.集合名.update({name: '张三'}, {$set:{age: 19}})
    

    更新文档

  • 删除文档

    db.集合名.remove(查询条件)
    

    删除文档

4.4 应用场景

(1)新增
  • 用户注册
  • 发布视频
  • 发布商品
  • 发朋友圈
  • 发评论
  • 发微博
  • 发弹幕
(2)删除
  • 删除评论
  • 删除商品
  • 删除文章
  • 删除视频
  • 删除微博
(3)更新
  • 更新个人信息
  • 修改商品价格
  • 修改文章内容
(4)查询
  • 商品列表
  • 视频列表
  • 朋友圈列表
  • 微博列表
  • 搜索功能

五、Mongoose

5.1 介绍

  • Mongoose 是一个对象文档模型(ODM)库(Mongoose 官网),它是对 Node.js 原生的 MongoDB 模块进行了进一步的优化封装
  • Mongoose 是一个让我们可以通过 Node.js 来操作 MongoDB 数据库的一个模块
  • 大多数情况下,它被用来把结构化的模式应用到一个 MongoDB 集合,并提供了验证和类型转换等好处
  • 基于 MongoDB 驱动,通过关系型数据库的思想来实现非关系型数据库

5.2 作用

方便使用代码去操作 MongoDB 数据库

优势:

  1. 为文档创建模式结构(Schema),也可以说是约束
  2. 对模型中的对象/文档进行验证
  3. 数据可以通过类型转换为对象模型
  4. 可以使用中间件来应用业务逻辑挂钩
  5. 相比 MongoDB 驱动更容易

5.3 使用流程

  1. 通过 npm 命令去安装 Mongoose:
npm i mongoose
  1. 项目中引入 mongoose:
const mongoose = require('mongoose')
  1. 连接 MongoDB 数据库:
mongoose.connect('mongodb://数据库ip地址:端口号(默认端口27017可以省略)/数据库名')
(1)mongoose 初体验

使用 mongoose 完成对 MongoDB 数据库的连接

代码示例:

// 1.安装 mongoose ==>  npm i mongoose
// 2.导入 mongoose
const mongoose = require('mongoose')

// 3.连接 mongodb 服务(mongodb 服务的默认端口是27017,所以可写可不写)
// mongodb://127.0.0.1:27017/bilibili 分别是:协议名称  IP地址  端口号  数据库名称
mongoose.connect('mongodb://127.0.0.1:27017/bilibili')

// 4.设置回调
// 设置连接成功的回调
mongoose.connection.on('open', () => {
    console.log('连接成功')
})

// 设置连接错误的回调
mongoose.connection.on('error', () => {
    console.log('连接失败')
})

// 设置连接关闭的回调
mongoose.connection.on('close', () => {
    console.log('连接关闭')
})

// 关闭 mongodb 的连接
setTimeout(() => {
    mongoose.disconnect()
}, 2000)
(2)创建新文档

在实现使用 mongoose 对 MongoDB 数据库连接的基础上,完成如下的操作:

  • 创建文档的结构对象(Schema)

    let BookSchema = new mongoose.Schema({
    	// 设置集合中文档的属性以及属性值的类型	例如:
        name: String,
        price: Number
        // .....
    })
    
    • 通过 Schema 创建 Model
    • Model 代表的是数据库中的集合,通过 Model 才能对数据库进行操作
  • 创建模型对象(Model)

    let BookModel = mongoose.model('books', BookSchema)
    

    参数:

    1. 要映射的集合名称
    2. 创建的约束(Schema 对象)
  • 通过 Model 返回的值对数据进行增、删、改、查(这里实现的是增)

    BookModel.create({
    	name: '西游记',
    	author: '吴承恩',
    	price: 19.9
    }).then((data) => {
    	console.log(data)   // 插入成功
    }).catch((err) => {
    	console.log(err)    // 插入失败
    })
    

    注意:高版本的 mongoose 的 create 方法是没有第二个参数(即回调函数),而是返回了一个 Promise 对象,所以我们需要采用 then 和 catch 方法来捕获状态。

  • 断开数据库连接(程序运行过程中,一般不会使用)

    mongoose.disconnect()
    

代码示例:

// 1.安装 mongoose
// 2.导入 mongoose
const mongoose = require('mongoose')

// 3.连接 mongodb 服务
mongoose.connect('mongodb://127.0.0.1:27017/bilibili')

// 4.设置回调
// 设置连接成功的回调
mongoose.connection.once('open', () => {
    // console.log('连接成功')
    // 5.创建文档的结构对象(Schema:模式,可以理解为结构)
    // 设置集合中文档的属性以及属性值的类型
    let BookSchema = new mongoose.Schema({
        name: String,
        author: String,
        price: Number
    })

    // 6.创建模型对象(模型对象是对文档操作的封装对象)
    // 参数1:集合名称      参数2:结构对象
    let BookModel = mongoose.model('books', BookSchema)

    // 7.新增
    /**
     * 报错问题:MongooseError: Model.create() no longer accepts a callback
     * 原因:在旧版本中 Model.create() 的第二个参数是一个回调函数,用来捕获成功或异常;但是在最新
     *       版本中,Model.create() 中没有第二个参数了,而是返回了一个 Promise 对象,我们将采用
     *       then 方法和 catch 方法来捕获状态
     * 
     * then:捕获成功
     * catch:捕获失败
     */
    BookModel.create({
        name: '西游记',
        author: '吴承恩',
        price: 19.9
    }).then((data) => {
        console.log(data)   // 增加成功
        // 8.关闭数据库连接(项目运行过程中,不会添加该代码)
        mongoose.disconnect()
    }).catch((err) => {
        console.log(err)    // 增加失败
    })
})

// 设置连接错误的回调
mongoose.connection.on('error', () => {
    console.log('连接失败')
})

// 设置连接关闭的回调
mongoose.connection.on('close', () => {
    console.log('连接关闭')
})

5.4 字段类型

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

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

代码示例:

// 导入 mongoose
const mongoose = require('mongoose')

// 连接 mongodb 服务
mongoose.connect('mongodb://127.0.0.1:27017/mongoose_test')

// 设置回调
// 设置连接成功的回调
mongoose.connection.once('open', () => {
    // 创建文档的结构对象
    let BookSchema = new mongoose.Schema({
        name: String,
        author: String,
        price: Number,
        is_hot: Boolean,
        // 关于数组类型的标识:除了使用 Array 外,还可以使用 [] 来标识
        tags: [],
        pub_time: Date,
        test: mongoose.Schema.Types.Mixed
    })

    // 创建模型对象
    let BookModel = mongoose.model('books', BookSchema)

    // 新增
    BookModel.create({
        name: '西游记',
        author: '吴承恩',
        price: 19.9,
        // 属性名被写错时,那么该属性名就会被忽略
        is_hot: true,     // 如果写的"true"或"false",会自动转换为布尔值
        tags: ['鬼怪', '神话', '励志'],
        pub_time: new Date(),
        test: 99
    }).then((data) => {
        console.log(data)   // 增加成功
        // 关闭数据库连接
        mongoose.disconnect()
    }).catch((err) => {
        console.log(err)    // 增加失败
    })
})

// 设置连接失败的回调
mongoose.connection.on('err', () => {
    console.log('连接失败')
})

// 设置连接关闭的回调
mongoose.connection.on('close', () => {
    console.log('连接关闭')
})

5.5 字段值验证

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

(1)必填项
// 给 name 属性设置必填项,就必须给 name 赋值,否则会报错(name: Path `name` is required)
name: {
	type: String,
	required: true  // 设置必填项
}
(2)默认值
// 给 author 属性设置默认值
author: {
	type: String,
	default: '匿名' // 设置默认值(未设置属性值时,会使用该默认值)
}
(3)枚举值
// 给 style 属性设置枚举值
style: {
	type: String,
	enum: ['神话', '小说', '鬼怪']  // 设置的属性值都必须是该数组中的,否则会报错(style: `a` is not a valid enum value for path `style`)
}
(4)唯一值
// 给 username 设置唯一值,那么该属性的属性值是独一无二的,否则会发生报错
username: {
	type: String,
	unique: true    // 注:unique 需要重新创建集合才能有效果
}

unique 需要 重建集合 才能有效果

开发定理:永远不要相信用户的输入

5.6 CURD

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

(1)增加(create)
  • 增加一条数据:(create)

    BookModel.create({
        name: '西游记',
        author: '吴承恩',
        price: 19.9,
        is_hot: true
    }).then((data) => {
        console.log(data)
    }).catch((err) => {
        console.log(err)
    })
    
  • 增加多条数据:(insertMany)

    BookModel.insertMany([
        {
            name: '与病毒同行',
            author: '沐日海洋',
            price: 9.9,
            is_hot: true
        },
        {
            name: '末日技能树',
            author: '暗黑茄子',
            price: 9.9,
            is_hot: true
        }
    ]).then((data) => {
        console.log(data)
    }).catch((err) => {
        console.log(err)
    })
    
(2)删除(delete)
  • 删除一条数据:(deleteOne)

    BookModel.deleteOne({_id: '65f7af461fe13910a107685f'}).then((data) => {
        console.log(data)
    }).catch((err) => {
        console.log(err)
    })
    
  • 删除多条数据:(deleteMany)

    BookModel.deleteMany({is_hot: false}).then((data) => {
        console.log(data)
    }).catch((err) => {
        console.log(err)
    })
    
(3)修改(update)
  • 修改一条数据:(updateOne)

    BookModel.updateOne({name: '红楼梦'}, {price: 9.9}).then((data) => {
        console.log(data)
    }).catch((err) => {
        console.log(err)
    })
    
  • 修改多条数据:(updateMany)

    BookModel.updateMany({author: '余华'}, {is_hot: false}).then((data) => {
        console.log(data)
    }).catch((err) => {
        console.log(err)
    })
    
(4)查询(read)
  • 查询一条数据:

    • findOne:

      BookModel.findOne({author: '沐日海洋'}).then((data) => {
          console.log(data)
      }).catch((err) => {
          console.log(err)
      })
      
    • findById:

      // 还可以使用 findById(根据 id 查询数据)
      BookModel.findById('65f7cf5bef9a5f1f2084bc90').then((data) => {
          console.log(data)
      }).catch((err) => {
          console.log(err)
      })
      
  • 查询多条数据:(find)

    • 不加条件查询(查询所有)

      BookModel.find().then((data) => {
          console.log(data)
      }).catch((err) => {
          console.log(err)
      })
      
    • 加条件查询

      BookModel.find({price: 9.9}).then((data) => {
          console.log(data)
      }).catch((err) => {
          console.log(err)
      })
      

5.7 条件控制

(1)运算符

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

  • > 使用 $gt
  • < 使用 $lt
  • >= 使用 $gte
  • <= 使用 $lte
  • !== 使用 $ne
BookModel.find({price: {$lt: 10}}).then((data) => {
    console.log(data)
}).catch((err) => {
    console.log(err)
})
(2)逻辑运算
  • $or 逻辑或的情况

    BookModel.find({$or: [{author: '余华'}, {author: '沐日海洋'}]}).then((data) => {
        console.log(data)
    }).catch((err) => {
        console.log(err)
    })
    
  • $and 逻辑与的情况

    BookModel.find({$and: [{price: {$gt: 30}}, {price: {$lt: 70}}]}).then((data) => {
        console.log(data)
    }).catch((err) => {
        console.log(err)
    })
    
(3)正则匹配

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

// 语法格式为:(key 表示实现模糊查询的关键字)
// 比如:搜索书籍名称中带有‘三’的图书,那么 key 就为‘三’
BookModel.find({属性名: /key/})

BookModel.find({name: //}).then((data) => {
    console.log(data)
}).catch((err) => {
    console.log(err)
})

另一种写法:

BookModel.find({name: new RegExp('三')}).then((data) => {
    console.log(data)
}).catch((err) => {
    console.log(err)
})

有些时候关键字究竟是什么,你不确定,它可能放在一个变量当中,而如果是变量的话,将其放在‘/key/’上是无法解析,那么就需要使用 new RegExp(‘key’) 这种方式了

5.8 个性化读取

(1)字段筛选
// 通过 select 可以设置字段,来实现字段筛选 —— 格式:select({属性名: 1}) / select({属性名: 0})
// 0:不要的字段
// 1:要的字段
BookModel.find().select({name: 1, author: 1, _id: 0}).then((data) => {
    console.log(data)
}).catch((err) => {
    console.log(err)
})
(2)数据排序
// 通过 sort 可以实现数据排序 —— 格式:sort({属性名: 1}) / sort({属性名: -1})
// 1:升序
// -1:降序
BookModel.find().select({name: 1, price: 1, _id: 0}).sort({price: -1}).then((data) => {
    console.log(data)
}).catch((err) => {
    console.log(err)
})
(3)数据截取
// 通过 skip 可以实现跳过数据	—— 格式:skip(n) --- 跳过 n 条数据
// 通过 limit 可以实现限定获取数据 —— 格式:limit(n) --- 限定获取 n 条数据
// 通常情况下,我们会将 skip 和 limit 一起使用,用来实现分页器
BookModel.find().select({name: 1, price: 1, _id: 0}).sort({price: -1}).skip(3).limit(3).then((data) => {
    console.log(data)
}).catch((err) => {
    console.log(err)
})

5.9 mongoose 模块化

经过前面的学习,我们会发现有很多代码是一直在重复的书写,这在实际开发中,是很浪费时间的,所以我们需要对 mongoose 模块化,将一些重复的代码提取出来并将其封装起来:

  1. 创建配置文件夹,再创建配置文件 config.js,然后将IP地址、端口号以及数据库名称写入该文件中:(以后便于修改)

    创建配置文件

    // 配置文件
    module.exports = {
        DBHOST: '127.0.0.1',
        DBPORT: 27017,
        DBNAME: 'mongoose_test'
    }
    
  2. 创建 db 文件夹,再创建 db.js 文件,然后将以下代码写入 db.js 文件中:(导入配置文件,获取连接数据库的参数)

    创建db

    /**
     * 
     * @param {*} success 数据库连接成功的回调
     * @param {*} error 数据库连接失败的回调
     */
    module.exports = function (success, error) {
        // 判断 error 为其设置默认值
        if(typeof error !== 'function') {
            error = () => {
                console.log('连接失败~~~')
            }
        }
        // 导入 mongoose
        const mongoose = require('mongoose')
        // 导入配置文件(解构赋值)
        const {DBHOST, DBPORT, DBNAME} = require('../config/config')
    
        // 连接 mongodb 服务
        mongoose.connect(`mongodb://${DBHOST}:${DBPORT}/${DBNAME}`)
    
        // 设置连接成功的回调
        mongoose.connection.once('open', () => {
            success()
        })
    
        // 设置连接错误的回调
        mongoose.connection.on('error', () => {
            error()
        })
    
        // 设置连接关闭的回调
        mongoose.connection.on('close', () => {
            console.log('连接关闭')
        })
    }
    
  3. 创建 model 文件夹,然后创建不同集合的 Model.js 文件,并写入创建结构对象和创建模型对象的代码:

    比如:创建集合 novel 的 BookModel.js 文件

    创建Model

    // 导入 mongoose
    const mongoose = require('mongoose')
    
    // 创建文档的结构对象(Schema:模式,可以理解为结构)
    let BookSchema = new mongoose.Schema({
        name: String,
        author: String,
        price: Number
    })
    
    // 创建模型对象(模型对象是对文档操作的封装对象)
    let BookModel = mongoose.model('novel', BookSchema)
    
    // 暴露 BookModel
    module.exports = BookModel
    
  4. 最后,创建 index.js 并导入 db.js 和 Model.js,将对数据库的增删改查都在 index.js 中操作:

    // 导入 db 文件
    const db = require('./db/db')
    // 导入 mongoose
    const mongoose = require('mongoose')
    // 导入 BookModel
    const BookModel = require('./model/BookModel')
    
    // 调用 db 函数
    db(() => {
        // 查询
        BookModel.findOne({ author: '沐日海洋' }).then((data) => {
            console.log(data)
            // 关闭数据库连接
            mongoose.disconnect()
        }).catch((err) => {
            console.log(err)
        })
    })
    

六、图形化管理工具

我们可以使用图形化的管理工具来对 Mongodb 进行交互,这里演示两个图形化工具:

  • 10
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值