球球了主人,请把我当傻子调教吧系列之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操作系统通过虚拟内存建立内存映射文件
  • 工作集???
相关推荐
吴声子夜歌5 小时前
MongoDB 4.2——查询
数据库·mongodb
吴声子夜歌12 小时前
MongoDB 4.2——索引(二)
数据库·mongodb·索引
吴声子夜歌1 天前
MongoDB 4.x——并发优化
数据库·mongodb
吴声子夜歌1 天前
MongoDB 4.2——入门指南
数据库·mongodb
吴声子夜歌1 天前
MongoDB 4.x——高可靠
数据库·mongodb
码农学院1 天前
Node.js MongoDB构建户外用品GEO优化内容管理平台
数据库·mongodb·node.js
吴声子夜歌1 天前
MongoDB 4.x——治理经验
数据库·mongodb
吴声子夜歌2 天前
MongoDB 4.x——应用设计调优
数据库·mongodb
吴声子夜歌3 天前
MongoDB 4.x——高级特性(Change Stream和事务)
数据库·mongodb
zhangphil3 天前
MongoDB创建数据库时的Collection Name
mongodb