球球了主人,请把我当傻子调教吧系列之MongoDB数据库

啥是MongoDB?

  • MongoDB 是一种 面向文档的 NoSQL 数据库

啥是SQL?

  • Structured Query Language
  • 结构化查询语言
  • 一种专门用于操作关系型数据库的编程语言

啥是NOSQL?

  • Not Only SQL(不仅仅是SQL)
  • 泛指所有不主要使用sql语言,不严格使用表格结构的数据库

MongoDB,MySQL和Redis的区别是什么?

维度 MySQL MongoDB Redis
数据库类型 关系型数据库 文档型 NoSQL 数据库 键值对型 NoSQL 数据库(内存数据库)
设计哲学 绝对-数据的一致性 变捷-数据的灵活性 极速-数据的极致性能
数据存储位置 磁盘(主)+ 内存缓存 磁盘(主)+ 内存映射 内存(主)+ 磁盘(持久化备份)
存储格式 面向表格 面向文档 面向键值对(面向数据的结构)
存储架构 面向磁盘的页式存储系统 面向内存映射文件的文档存储系统 面向内存的数据结构服务器

MongoDB的存储结构是怎样的?如何理解他们?

存储结构:

css 复制代码
MongoDB 实例
├── 数据库(Database)
│   └── 集合(Collection)------ 类似MySQL的表,但无Schema约束
│       └── 文档(Document)------ JSON/BSON格式,可嵌套
│           ├── 字段1: 值
│           ├── 字段2: 数组 [值1, 值2]
│           └── 嵌套文档: {子字段1, 子字段2}

如何理解五层存储层级?

1.类比JS的对象
  • 一个数据库是一个巨大的对象 :你连接 MongoDB 时,拿到的 db 对象。
  • 一个集合是对象数组:`db.collection 就相当于一个数组,里面放了无数个对象。
  • 一个文档是数组里的一个对象{ field1: "...", field2: "...", } 就是数组里的具体对象。
  • 一个字段是对象的属性doc.field1doc.field2。 (严谨地讲)有两处微小的物理差异

MongoDB 服务器在物理存储(磁盘层面) 和 JavaScript 数组有两点不同:

  • 差异一:数组有顺序,集合没有固定顺序

    • JS 数组是 [0号, 1号, 2号],有严格的下标。
    • MongoDB 集合里的文档是"无序"的(除非你特意用 { $sort } 排序)。你不能说"把第 0 个文档拿出来",你只能说"把 _id 为 xxx 的文档拿出来"。
  • 差异二:数组在内存里,集合在磁盘里

    • JS 数组是一次性全部加载到内存里的。
    • MongoDB 的集合可能大到几百个 GB,它是一点一点从磁盘读到内存(通过游标)给你的,不会一口气全部倒出来。
2.类比Mysql的存储结构

整体层次对比

层次 MongoDB MySQL (InnoDB)
第一层 实例(一个 mongod 进程) 实例(一个 mysqld 进程)
第二层 数据库(Database) 数据库(Schema / Database)
第三层 集合(Collection) (Table)
第四层 文档(Document) (Row)
第五层 字段(Field) 列(Column)

对应关系

  • MongoDB 的 集合 → MySQL 的 表
  • MongoDB 的 文档 → MySQL 的 行
  • MongoDB 的 字段 → MySQL 的 列

最大区别

  • MongoDB:同一个集合里的不同文档,可以有完全不同的字段
  • MySQL:同一个表里的所有行,必须有完全相同的列结构(预定义的,结构化的)

啥是BSON?

BSONBinary JSON 的缩写,是一种二进制编码的序列化格式,主要用于 MongoDB 的数据存储和网络传输。

简单说:BSON = JSON 的二进制版本。

Moongodb数据库的基本CRUD操作:

一.准备工作

1. 启动 MongoDB 的命令行

复制代码
mongosh

2. 查看所有数据库

sql 复制代码
show dbs

3. 切换到指定的数据库(存在则切换,不存在则创建)

rust 复制代码
use myApp//切换或创建一个名为myApp的数据库

4. 查看当前数据库的所有集合

sql 复制代码
show collections

二、C - Create(创建文档)

给某个集合插入单条文档

javascript 复制代码
db.collection1.insertOne({//collection1是你给集合的命名,内容任意
    title: "后悔大学没好好学英语",
    content: "现在看外文文档非常吃力",
    candles: 5,
    position: { x: 30, y: 50 }
})

返回结果

javascript 复制代码
{
    acknowledged: true,//创建成功
    insertedId: ObjectId("65d8a1b2c3d4e5f6g7h8i9j0")  // MongoDB 自动生成的 _id
}

给某个集合插入多条文档

javascript 复制代码
db.collection2.insertMany([
    {
        title: "后悔没去留学",
        content: "当年有机会却没去,现在工作后没时间了",
        candles: 5,
        position: { x: 20, y: 60 }
    },
    {
        title: "后悔没早点学编程",
        content: "28岁才转行,要是早几年就好了",
        candles: 12,
        position: { x: 80, y: 40 }
    }
])

三、R - Read(读取文档)

1. 查询某个集合的所有文档

javascript 复制代码
db.collection1.find({})

返回一个游标(可以理解为"结果集"),里面是所有文档:

yaml 复制代码
[
  {
    _id: ObjectId('6a312d7717f717958fabc114'),
    title: '后悔大学没好好学英语',
    content: '现在看外文文档非常吃力',
    candles: 5,
    position: { x: 30, y: 50 }
  },
  {
    .......
  }
]

美化输出(让显示更整齐):

javascript 复制代码
db.regrets.find({}).pretty()

2. 按条件查询

javascript 复制代码
// 查询标题为"后悔没学好英语"的文档
db.collection1.find({ title: "后悔没学好英语" })

// 查询 candles 大于 5 的文档
db.collection1.find({ candles: { $gt: 5 } })

// 查询 candles 在 3 到 10 之间的文档
db.collection2.find({ candles: { $gte: 3, $lte: 10 } })

3. 查询单条文档

javascript 复制代码
// 按 _id 查(最常用)
db.collection2.findOne({ _id: ObjectId("65d8a1b2c3d4e5f6g7h8i9j0") })

4. 条件组合查询

javascript 复制代码
// 查询 candles 大于 3,且 position.x 小于 50 的文档
db.collection2.find({
    candles: { $gt: 3 },
    "position.x": { $lt: 50 }   // 注意嵌套字段要用引号包起来
})

5. 常见的查询操作符

操作符 含义 例子
$eq 等于 { candles: { $eq: 5 } }
$gt 大于 { candles: { $gt: 5 } }
$gte 大于等于 { candles: { $gte: 5 } }
$lt 小于 { candles: { $lt: 5 } }
$lte 小于等于 { candles: { $lte: 5 } }
$ne 不等于 { candles: { $ne: 5 } }
$in 在数组中 { title: { $in: ["后悔A", "后悔B"] } }
$and 逻辑与 { $and: [{ candles: { $gt: 3 } }, { candles: { $lt: 10 } }] }
$or 逻辑或 { $or: [{ title: "后悔A" }, { title: "后悔B" }] }

6. 投影(只返回指定字段)

javascript 复制代码
// 只返回 title 和 candles,不返回 content
db.collection2.find({}, { title: 1, candles: 1, _id: 0 })
// 1 表示返回,0 表示不返回

7. 排序、限制、跳过

javascript 复制代码
// 按 candles 升序排序(1 升序,-1 降序)
db.collection2.find({}).sort({ candles: 1 })

// 只返回前 5 条
db.collection2.find({}).limit(5)

// 跳过前 10 条(分页用)
db.collection2.find({}).skip(10)

// 组合使用:按蜡烛数降序,取第 11 到 20 条
db.collection2.find({}).sort({ candles: -1 }).skip(10).limit(10)

四、U - Update(更新文档)

1. 更新单条文档

javascript 复制代码
// 找到 candles 为 0 的第一条,把 candles 改成 1
db.collection2.updateOne(
    { candles: 0 },              // 查询条件
    { $set: { candles: 1 } }     // 更新操作
)

2. 更新多条文档

javascript 复制代码
// 把所有 candles 为 0 的文档,都改成 1
db.collection2.updateMany(
    { candles: 0 },
    { $set: { candles: 1 } }
)

3. 常用更新操作符

操作符 含义 例子
$set 设置字段值 { $set: { title: "新标题" } }
$inc 数字增减 { $inc: { candles: 1 } } (蜡烛数 +1)
$unset 删除字段 { $unset: { position: "" } }
$push 往数组里加元素 { $push: { tags: "人生" } }
$pull 从数组里移除元素 { $pull: { tags: "人生" } }

4. 更新并返回更新后的文档

javascript 复制代码
// findOneAndUpdate:找到并更新,返回更新后的文档
db.collection2.findOneAndUpdate(
    { title: "后悔没学好英语" },
    { $inc: { candles: 1 } },
    { returnDocument: "after" }   // "after" 返回更新后的,"before" 返回更新前的
)

5. 替换整个文档(不推荐,一般用 $set)

javascript 复制代码
// 把整个文档替换成新的内容(危险操作,会删除原有字段)
db.collection2.replaceOne(
    { title: "后悔没学好英语" },
    { title: "新标题", content: "新内容" }   // 旧文档里的 candles、position 都没了
)

五、D - Delete(删除文档)

1. 删除单条文档

javascript 复制代码
// 删除标题为"后悔没学好英语"的第一条
db.collection2.deleteOne({ title: "后悔没学好英语" })

// 按 _id 删除
db.collection2.deleteOne({ _id: ObjectId("65d8a1b2c3d4e5f6g7h8i9j0") })

2. 删除多条文档

javascript 复制代码
// 删除所有 candles 为 0 的文档
db.collection2.deleteMany({ candles: 0 })

// 删除集合里的所有文档(危险!)
db.collection2.deleteMany({})

3. 删除并返回被删除的文档

javascript 复制代码
// findOneAndDelete:找到并删除,返回被删除的文档
db.collection2.findOneAndDelete({ title: "后悔没学好英语" })

什么是mongoose?

Mongoose 是一个 Node.js 的 MongoDB ODM(对象文档映射)库,用于在 Node.js 应用中操作 MongoDB 数据库。

说人话就是一个允许你在 Node.js 应用中操作 MongoDB 数据库的高级客户端。

mongoose操作mongoDB数据库的基本语法:


1. 定义 Schema 与 Model(模型)

javascript 复制代码
import mongoose from "mongoose";

// 1. 定义 Schema(数据结构蓝图)
const collection2Schema = new mongoose.Schema(
  {
    // 字段定义区
    title: {
      type: String,//字段数据类型
      required: true,//插入文档时是否必须存在该字段,
      //底层会检查 `title` 是否为 `null` 或 `undefined`,如果是则抛出 `ValidationError`。
      trim: true,//插入文档时是否在存库前自动去掉字符串首尾的空格。
      maxlength: 40、、验证字符串长度不能超过 40。超过则报错。
    },
    content: {
      type: String,
      required: true,
      trim: true,
      maxlength: 1000
    },
    candles: {
      type: Number,
      default: 0,//插入文档时,如果没提供 `candles` 字段,Mongoose 会自动填入 `0`。
      //这是一个默认值,不由 MongoDB 处理,而是由 Mongoose 在入库前填充。
      min: 0//设置字段最小值,如果尝试存入 `-5`,会触发验证错误。
    },
    //这里没有加 `required: true`,`position` 字段是可选的。插入时可以不传,或者只传 `x` 不传 `y`。
    position: {
      x: Number,
      y: Number
    },
    // 引用文档示例(假设指向 User)
    author: {
      type: mongoose.Schema.Types.ObjectId,//存储的只是Id,不是数据本身
      ref: "User"//这是**引用(Reference)**  的声明。
      /*它告诉 Mongoose:"这个 `author` 字段存的 ID,指向的是 `User` 这个 Model(也就是 `users` 集合)里的某条文档"。*/
    }
  },
  {
    // 配置选项区
    timestamps: { createdAt: true, updatedAt: false },
    //控制插入文档时Mongoose 是否自动加 `createdAt`和`updateAt` 字段,
    //creatAt值为文档第一次被插入数据库时记录的时间(一辈子只记录一次),
    //updateAt值为文档每一次被修改并保存时,自动更新为"当前时间"(随时在变)。
    versionKey: false
    //Mongoose 默认会给每个文档加一个 `__v` 字段,每次修改文档并 `.save()` 时,`__v` 会自增 1
    //解决冲突编辑风险
  }
);

// 2. 导出 Model(注意:这里集合名会被命名为 "collection2")
export default mongoose.model("collection2", collection2Schema);
什么是Schema?它在mongoDB和Mysql两个不同的数据库上有什么不同?
  • Schema是一种规范,它规定了集合(表)里的文档(行)应该有哪些字段、什么类型、是否必填等相关信息。
  • Schema ≠ Database(数据库容器)
  • 只是在 MySQL 里,CREATE SCHEMA 被当成了 CREATE DATABASE 的同义词,这才导致了混淆。
mongoDB语境下的Schema:
1. 字段定义区
  • 基础类型StringNumber 等,并附带验证(requiredmaxlengthmin)。
  • 嵌套文档position: { x: Number, y: Number } ------ 数据内嵌,逻辑上属于父文档。
  • 引用文档author: { type: ObjectId, ref: 'User' } ------ 只存(ID),数据实际在别的集合。
2. 配置选项区
  • timestamps :告诉 Mongoose 自动维护时间戳(createdAt 记录出生,updatedAt 记录最近一次修改)。
  • versionKey: false :控制是否记录乐观锁版本号(__v

Schema 是挂载在 Collection(集合) 上,但检查的是 Document(文档) 下具体的每个 Field(字段) 。MongoDB 服务器本身只认 _id 唯一性,其他规则全靠 Mongoose 在应用层拦截。

mysql语境下的Schema:
  • 容器层:你可以在这里认为Schema = Database,因为CREATE SCHEMA 相当于 CREATE DATABASE
  • 结构层:和mongoDB一样,是一种约束数据结构的规范。只不过在mysql里schema 是必须要有的。
什么是引用文档?和嵌套文档有什么区别?

文档里只存一个 "ObjectId ",真正的数据在另一个集合里。

arduino 复制代码
// ========== 嵌套文档 ==========
// 位置信息完全写在遗憾文档内部
{
  _id: "regret_001",
  title: "后悔没学英语",
  position: {          // ← 这就是嵌套
      x: 45.6,
      y: 120.3
  }
}

// ========== 引用文档 ==========
// 遗憾文档里只存作者的 ID
{
  _id: "regret_002",
  title: "后悔没告白",
  author_id: "user_007"   // ← 这就是引用(存的是别人的 ID),字段名自定义
}
// 作者的真实姓名、头像,存在另一个叫 users 的集合里

引用文档在Schema里的实例(假设指向 User)

rust 复制代码
    author: {
      type: mongoose.Schema.Types.ObjectId,//存储的只是Id,不是数据本身
      ref: "User"//这是**引用(Reference)**  的声明。
      /*它告诉 Mongoose:"这个 `author` 字段存的 ID,指向的是 `User` 这个 Model(也就是 `users` 集合)里的某条文档"。*/

查询时,用 .populate() 自动填充:

csharp 复制代码
// 业务代码
const regret = await Regret.findById('reg_001').populate('author');
console.log(regret.author); 
// 打印出来的不再是 ObjectId("user_007"),而是:
// { _id: 'user_007', name: '张伟', avatar: 'avatar.jpg', email: '...' }之类的

 const regret = await Regret.findById(req.params.id).populate('author', 'name avatar'); 
 // 第二个参数表示:只取用户的 name 字段和 avatar字段,不要 email,节省流量!
到底怎么选?

"这个子数据,是永远只属于这一个父数据,还是会被很多父数据共用?"

  • 如果"只属于一个"必须用嵌套文档

    • 例子:订单里的收货地址、购物车里的商品列表。地址不会同时属于两个订单,所以直接嵌进去。
  • 如果"会被很多个共用"必须用引用文档

    • 例子:作者信息、商品分类、文章标签。一个作者会写几百篇文章,总不能把作者的名字复制几百遍吧?

2. 增(Create)

javascript 复制代码
import Collection2 from "./models/collection2.js";

// 插入一条文档
const newDoc = await Collection2.create({
    title: "后悔大学没好好学英语",
    content: "现在看外文文档非常吃力",
    candles: 5,
    position: { x: 30, y: 50 }
});

// 批量插入多条文档(不常用,但语法如此)
const manyDocs = await Collection2.insertMany([
    { title: "后悔没早买房", content: "房价涨了三倍", candles: 100 },
    { title: "后悔没告白", content: "她成了别人的新娘", candles: 66 }
]);

3. 查(Read)

javascript 复制代码
// 查询所有文档
const all = await Collection2.find();

// 精确条件查询(等值匹配)
const exact = await Collection2.find({ title: "后悔没告白" });

// 比较查询(大于 $gt)
const gt = await Collection2.find({ candles: { $gt: 50 } });

// 多条件"且"查询(嵌套字段用引号包裹)
const andQuery = await Collection2.find({ 
    candles: { $gt: 10 }, 
    "position.x": 30 
});

// 多条件"或"查询($or)
const orQuery = await Collection2.find({
    $or: [
        { title: "后悔没买房" },
        { candles: { $lt: 5 } }
    ]
});

// 只返回特定字段(投影):只要 title 和 candles,不要 _id
const projection = await Collection2.find(
    { candles: { $gt: 10 } },
    { title: 1, candles: 1, _id: 0 }
);

// 按 ObjectId 精确查找单条
const byId = await Collection2.findById("65f2a1b3c4d5e6f7a8b9c0d1");

// 查找单条(按条件)
const one = await Collection2.findOne({ title: "后悔没告白" });

// 排序 + 限制条数(按蜡烛数降序,取前3条)
const sorted = await Collection2.find()
    .sort({ candles: -1 })  // -1 降序,1 升序
    .limit(3);

// 统计文档数量(全部)
const totalCount = await Collection2.countDocuments();
// 统计符合条件的文档数量
const condCount = await Collection2.countDocuments({ candles: 50 });

// 【高级】关联查询(填充引用文档,即 Populate)
// 假设 author 字段存的是 User 的 _id
const withAuthor = await Collection2.find().populate("author");
// 这样拿到的结果里,author 不再是一个 ObjectId,而是完整的 User 对象。

4. 改(Update)

javascript 复制代码
// 修改单个文档(局部更新,使用 $set)
await Collection2.updateOne(
    { title: "后悔没告白" },                // 条件
    { $set: { content: "她成了别人的新娘,新郎竟然是我哥们" } } // 修改内容
);

// 数字字段增加($inc,常用于点赞/蜡烛数)
await Collection2.updateOne(
    { title: "后悔没告白" },
    { $inc: { candles: 1 } }               // 蜡烛数 +1
);

// 修改多个文档(批量更新)
await Collection2.updateMany(
    { candles: { $lt: 10 } },              // 条件:蜡烛少于10
    { $set: { status: "冷门遗憾" } }        // 给这些文档加一个新字段 status
);

// 查找并更新(原子操作,常用)
// new: true 表示返回更新后的新文档,否则返回旧文档
const updated = await Collection2.findByIdAndUpdate(
    "65f2a1b3c4d5e6f7a8b9c0d1",            // ID
    { $set: { title: "新标题" } },
    { new: true }
);

5. 删(Delete)

javascript 复制代码
// 删除单条文档(按条件)
await Collection2.deleteOne({ title: "后悔没早买房" });

// 删除单条文档(按 ID)
await Collection2.findByIdAndDelete("65f2a1b3c4d5e6f7a8b9c0d1");

// 删除多条文档(按条件)
await Collection2.deleteMany({ candles: { $lt: 0 } });

// 【高危】删除整个集合(慎用!)
// 在 Mongoose 里通常用原生方法
await Collection2.collection.drop();

6. 嵌套文档与引用的写法补充

  • 定义嵌套文档(就像你的 position :直接在 Schema 里写 { x: Number, y: Number } 即可。

  • 定义引用文档(关联其他集合)

    javascript 复制代码
    author: {
        type: mongoose.Schema.Types.ObjectId,
        ref: "User"  // 这里 "User" 必须和你导出的 User 模型名完全一致
    }

总结速查表(纯享版)

业务动作 Mongoose 写法 (Collection2 模型)
查全部 Collection2.find()
按条件查 Collection2.find({ title: "..." })
按 ID 查 Collection2.findById(id)
查一条 Collection2.findOne({ ... })
新增 Collection2.create({ ... })
改一条 Collection2.updateOne({条件}, {$set:{...}})
加数字 Collection2.updateOne({条件}, {$inc:{candles:1}})
改多条 Collection2.updateMany({条件}, {$set:{...}})
删一条 Collection2.deleteOne({条件})
删多条 Collection2.deleteMany({条件})
统计 Collection2.countDocuments({条件})
排序/限制 Collection2.find().sort({candles:-1}).limit(3)
关联填充 Collection2.find().populate("author")

现在所有命令都集中在 Collection2 上了,你可以直接复制粘贴使用。如果还需要其他特定操作的写法(比如数组操作 $push$pull),随时告诉我。

实际操作速查对比表(MongoDB Shell vs Mongoose)

操作 MongoDB Shell Mongoose (你的项目)
插入单条 db.collection2.insertOne({...}) db.Collection2.create({...})
插入多条 db.collection2.insertMany([...]) await Collection2.insertMany([...])
查询所有 db.collection2.find({}) await Collection2.find({})
按条件查询 db.collection2.find({ candles: { $gt: 5 } }) await Collection2.find({ candles: { $gt: 5 } })
按 _id 查 db.collection2.findOne({ _id: ObjectId("...") }) await Collection2.findById("...")
更新单条 db.collection2.updateOne({...}, { $set: {...} }) await Collection2.updateOne({...}, { $set: {...} })
更新并返回 db.collection2.findOneAndUpdate({...}, {...}, { returnDocument: "after" }) await Collection2.findByIdAndUpdate("...", {...}, { new: true })
删除单条 db.collection2.deleteOne({...}) await Collection2.deleteOne({...})
按 _id 删 db.collection2.deleteOne({ _id: ObjectId("...") }) await Collection2.findByIdAndDelete("...")

我也不是很懂的mongoDB底层:(我在胡说霸道)

列几个关键字就这样吧,哈哈哈哈哈哈

  • mongoDB:os操作系统通过虚拟内存建立内存映射文件
  • 工作集???
相关推荐
H__Rick18 小时前
C51单片机学习-DAY3
单片机·学习·mongodb
霖霖总总1 天前
[MongoDB小技巧10]MongoDB 数组查询深度解析:$size、$all 与 $in 的核心机制与避坑指南
数据库·mongodb
thinking_talk1 天前
2026中国MongoDB云服务厂商能力榜:选型对比与效能评估
数据库·mongodb·腾讯云
霖霖总总1 天前
[MongoDB小技巧09]深入浅出 MongoDB 逻辑运算符:$and、$or、$nor、$not 原理与实战
数据库·mongodb
代码小库2 天前
【2026前端转 AI 全栈指南】第 2 章(下):NestJS 项目创建 · MongoDB 配置 · 项目启动与调试
前端·数据库·mongodb
jieyucx2 天前
Go MongoDB 实战完全指南|从连接、CRUD、BSON结构体映射到高并发避坑全解
开发语言·mongodb·golang
葡萄皮sandy2 天前
React Query+Zustand+Next.js + MongoDB全栈面试
mongodb·reactjs
葡萄皮sandy3 天前
NestJS + Mongoose 全栈开发面试总结
mongodb·面试
之歆3 天前
MongoDB 深度解析:从原理到实践的完整指南
数据库·mongodb