MongoDB

一、业务应用场景

传统的关系型数据库 (MySQL) 在数据操作的三高需求以及应对 Web 2.0 的网站需求面前显得力不从心,而 MongoDB 可完美应对该类需求:

  • High performance:对数据库高并发读写的需求
  • Huge Storage:对海量数据的高效率存储和访问的需求
  • High Scalability && High Avilaility:对数据库的高可扩展性和高可用性的需求

MongoDB 核心特性

  1. MySQL 需提前定义表结构、表关系,列固定,扩展代价高;MongoDB 为松散型结构,无需提前固定格式
  2. 文档型 NoSQL 数据库,适用于数据量大、写入操作频繁、价值较低(对事务性要求不高)的数据场景

二、数据类型

表格

数据类型 说明 示例 典型使用场景
字符串 (String) 存储 UTF-8 编码的文本,长度无硬性限制 "name": "MongoDB 指南 " 名称、描述、文本内容
整数 (int/long) int 为 32 位整数,long 为 64 位整数 (需显式声明) "age": 25"id": NumberLong("123456789") 年龄、计数、ID (大数值)
浮点数 (double) 64 位双精度浮点数,默认数值类型 "price": 99.99"weight": 65.5 价格、重量等带小数的数值
高精度小数 (Decimal) 高精度十进制数,适合金融计算 (避免浮点数精度丢失) "balance": NumberDecimal("10000.00") 金额、汇率等对精度敏感的场景
布尔值 (Boolean) 表示真 (true) 或假 (false) "isActive": true 状态标记 (是否启用、是否完成)
Null 表示空值或不存在的字段 "address": null 字段未设置或值为空的场景
日期 (Date) 存储 UTC 时间,精确到毫秒 (基于 Unix 时间) "createTime": new Date() 创建时间、过期时间、生日
嵌入式文档 文档中嵌套另一个文档,形成层级结构 "user":{"name":"张三","age": 30} 表示包含关系 (如用户包含详细信息)
数组 (Array) 有序值列表,元素可混合多种类型 (包括数组 / 文档) "tags":["数据库","NoSQL"] 标签、列表数据 (如爱好、成绩)
ObjectId 12 字节唯一标识符,默认作为文档主键_id "_id": ObjectId("60d21b4667d0d8992e610c85") 文档唯一标识
二进制数据 存储二进制数据 (如图片、文件),适合小文件 "avatar": BinData(,"base64数据") 存储小型二进制资源 (头像、证书)
正则表达式 存储正则表达式对象,用于字符串匹配查询 "pattern": /^Mongo/ 模糊查询条件定义
时间戳 (Timestamp) 64 位值,前 32 位为时间戳,后 32 位为操作计数器 (内部用于复制) "ts": Timestamp(1624300800, 1) MongoDB 内部复制和操作追踪
地理位置 (GeoJSON) 存储地理坐标 (点、线、面),支持地理空间查询 "location": { "type": "Point", "coordinates": [116.4, 39.9]} 位置信息 (如用户地址、店铺位置)
代码 (Code) 存储 JavaScript 代码,存储简单脚本逻辑 "script": Code("function() { return 1; }") 简单脚本执行
代码 + 作用域 存储 JavaScript 代码及关联的作用域 (变量环境) "script": CodeWithScope("function(){ return x; }", {x: 10}) 带变量的脚本执行

备注_id为 MongoDB 自动生成的主键字段

三、示例数据库 / 集合 / 字段定义

数据库:articledb

集合:专栏文章评论 comment

表格

字段名称 字段含义 字段类型 备注
_id ID ObjectId 或 String Mongo 的主键字段
articleid 文章 ID String -
content 评论内容 String -
userid 评论人 ID String -
nickname 评论人昵称 String -
createdatetime 评论的日期时间 Date -
likenum 点赞数 Int32 -
replynum 回复数 Int32 -
state 状态 String 0: 不可见;1: 可见
parentid 上级 ID String 如果为 0 表示文章的顶级评论

四、数据库操作

1. 选择和创建数据库

mongodb

复制代码
use 数据库名
  • 若数据库不存在则自动创建
  • 示例:use articledb
  • 规则:数据库名称需全部小写、不能有空格
  • 特性:创建后先存放在内存,当数据库有数据时会自动持久化到磁盘

2. 查看数据库

mongodb

复制代码
show dbs
# 或
show databases
系统默认数据库说明
  • admin:权限角度的 root 库,添加至此的用户自动继承所有数据库权限
  • local:数据永远不会被复制,用于存储本地单台服务器的任意集合
  • config:Mongo 用于分片设置时内部使用,保存分片相关信息

3. 删除数据库

mongodb

复制代码
db.getName() // 输出当前数据库名,确认是否为要删除的数据库
db.dropDatabase() // 删除已持久化的数据库

五、集合操作

集合类似关系型数据库中的

1. 显示创建

mongodb

复制代码
db.createCollection(名称)
# 示例
db.createCollection("mycollection")

2. 隐式创建

向不存在的集合中插入文档时,集合会自动创建:

mongodb

复制代码
// 插入单条文档,隐式创建 comment 集合
db.comment.insertOne({
"articleid":"100000", "content":"今天天气真好,阳光明媚",
"userid":"1001",
"nickname":"Rose",
"likenum":NumberInt(10), "createdatetime":new Date(),
"sate":null
})

3. 查看当前库中的集合

mongodb

复制代码
show collections
# 或
show tables

4. 删除集合

mongodb

复制代码
db.collection.drop() 
# 或
db.集合名.drop()
# 示例
db.mycollection.drop()

六、文档操作

(一)增:插入文档

1. 单个文档插入

mongodb

复制代码
db.集合名.insertOne(
{ 文档内容 }, // 要插入的文档(JSON格式)
{ 可选配置参数 } // 可选,如写入确认级别等
)
# 示例
db.comment.insert( {
"articleid":"100001", "content":"今天天气真好,阳光明媚",
"userid":"1001",
"nickname":"Rose",
"createdatetime":new Date(), "likenum":NumberInt(10),
"sate":null
})
2. 多个文档插入

mongodb

复制代码
db.集合名.insertMany([{文档1}, {文档2}, ...])
# 示例
db.comment.insertMany([
{
"_id": "1",
"articleid": "100001",
"content": "我们不应该把清晨浪费在手机上,健康很重要,一杯温水幸福你我他。",
"nickname": "相忘于江湖", 
"createdatetime": new Date("2019-08-05T22:08:15.522Z"), 
"likenum": NumberInt(1000), 
"userid": "1002",
"state": "1"
},
{
"_id": "2",
"articleid": "100002", 
"content": "我夏天空腹喝凉开水,冬天喝温开水",
"userid": "1005",
"likenum": NumberInt(888), 
"nickname": "伊人憔悴", 
"createdatetime": new Date("2019-08-05T23:58:51.485Z"),
"state": "1"
}
]);
批量插入关键特性
  • insertMany()默认ordered: true,非原子操作:若某文档插入失败,已成功的保留,后续的停止插入
  • 若设置ordered: false,会跳过失败文档,继续插入后续文档
  • 示例:_id冲突时,ordered: true后续文档不插入,ordered: false后续文档正常插入
插入通用规则
  • Mongo 中的数字默认是 double 类型,存储整型需使用NumberInt函数
  • 插入当前日期使用new Date()
  • 未指定_id时,Mongo 会自动生成主键值
  • insert返回结果:包含插入成功状态 (acknowledged: true) 和插入文档的_id

(二)删:删除文档

1. 删除单个文档

mongodb

复制代码
db.collection.deleteOne(
<filter>, // 查询条件,匹配要删除的文档
{
writeConcern: <document>, // 写入安全级别(可选)
collation: <document> // 字符串比较规则(可选)
}
)
# 示例1
db.comment.deleteOne({ articleid: "100005" });
# 示例2:指定写入安全级别
db.users.deleteOne(
{ name: "张三" },
{ writeConcern: { w: "majority", j: true, wtimeout: 1000 } }
)

writeConcern 参数说明{w:"majority",j:true,wtimeout:1000}

  1. 操作需同步到副本集的大多数节点
  2. 每个确认节点需将操作写入磁盘日志
  3. 1 秒内未满足上述条件则操作失败
2. 删除多个文档

mongodb

复制代码
db.collection.deleteMany(
<filter>, // 查询条件,匹配所有要删除的文档
{
writeConcern: <document>, // 可选
collation: <document> // 可选
}
)
# 示例:删除所有点赞数小于100的文档
db.comment.deleteMany({ likenum: { $lt: 100 } });
3. 查询并删除单个文档

mongodb

复制代码
db.collection.findOneAndDelete(
<filter>, // 查询条件
{
projection: <document>, // 指定返回的字段(可选)
sort: <document>, // 排序规则(多个匹配时按排序删除第一个)
writeConcern: <document>, // 可选
collation: <document> // 可选
}
)
# 示例:删除articleid为100005且likenum最小的文档,并返回该文档
db.comment.findOneAndDelete(
{ articleid: "100005" }, 
{ sort: { likenum: 1 } }
)
4. 清空集合文档(保留集合结构)

mongodb

复制代码
db.comment.remove({});

(三)改:更新文档

核心语法:

mongodb

复制代码
// 更新单个匹配文档
db.collection.updateOne(<filter>, <update>, <options>)
// 更新所有匹配文档
db.collection.updateMany(<filter>, <update>, <options>)
三个核心参数说明
  1. Filter(筛选器) :等同于find()的第一个参数,决定更新的文档;updateOne仅修改第一个匹配文档,updateMany修改所有匹配文档
  2. Update(更新操作) :必须使用更新操作符(以 $ 开头),禁止 "裸奔" 更新
  3. Options(配置项) :高阶配置,如upsert(无则加)、hint(强制索引)等
常用更新操作符

表格

操作符 作用 示例
$set 设定值,字段不存在则创建,存在则修改 {$set:{"age":18,"is_student":true}}
$unset 删除字段 {$unset:{"temporary_field":""}}
$inc 自增 / 自减数值 {$inc:{"score":10,"money":-5}}
$rename 重命名字段 {$rename:{"sex":"gender"}}
$push 向数组追加元素(允许重复) {$push:{"comments":"好文章"}}
$pull 从数组剔除指定元素 {$pull:{"comments":"这是垃圾广告"}}
$currentDate 更新时间字段 {$currentDate:{"lastdified":true}}
$mul 数值乘法运算 {$mul:{"likenum":1.2}}
$max 仅当新值大于当前值时更新 {$max:{"likenum":5000}}
$min 仅当新值小于当前值时更新 {$min:{"likenum":3000}}
$addToSet 向数组追加不重复元素 {$addToSet:{"tags":"生活小贴士"}}
$pop 删除数组第一个 / 最后一个元素 {$pop:{"arr":1}}(删最后)、{$pop:{"arr":-1}}(删最前)
$slice 配合 $push 使用,限制数组长度 {$push:{"logs":{"$each":["log3"],$slice:-3}}}
$setOnInsert 仅在 upsert 插入新文档时生效 {$setOnInsert:{"age":22}}
关键更新示例
  1. 多操作符组合更新

mongodb

复制代码
db.comment.updateOne(
{ _id: "5" },
{
$set: { content: "研究表明,刚烧开的水不宜立即饮用(避免烫伤)", authorLevel: "VIP" },
$inc: { likenum: 500 },
$rename: { "nickname": "username" },
$mul: { likenum: 1.2 },
$max: { likenum: 5000 },
$push: { tags: "健康知识" },
$addToSet: { tags: "生活小贴士" }
}
);
  1. 批量更新

mongodb

复制代码
db.comment.updateMany(
{ articleid: "100005", state: "1" }, // 查询条件
{
$set: { state: "2" },
$inc: { likenum: 50 },
$rename: { "nickname": "username" }
},
{ upsert: false } // 无匹配时不插入
);
  1. 无则加(upsert)

mongodb

复制代码
// 找到name为Eve的用户则更新,未找到则插入
db.users.updateOne(
{ name: "Eve" },
{ $set: { age: 22, email: "eve@example.com" } },
{ upsert: true }
)
  1. 数组专用更新

mongodb

复制代码
// 向数组批量追加元素
db.users.updateOne( { _id: 1 }, { $push: { hobbies: { $each: ["gaming", "painting"] } } } )
// 追加不重复元素
db.users.updateOne({id:1}, {$addToSet:{"tags":"帅"}})
// 按条件删除数组元素
db.users.updateOne( { _id: 2 }, { $pull: { scores: { score: 70 } } } )
// 追加后保留最后3个元素
db.logs.updateOne(
{_id:1},
{ $push: { "logs":{ $each:["log3","log4"], $slice:-3 } } }
)
5. 替换文档

用新文档完全替换第一个符合条件的文档(_id字段除外):

mongodb

复制代码
db.collection.replaceOne(<filter>, <replacement>, <options>)
# 示例
db.users.replaceOne(
{ name: "Bob" }, 
{ name: "Robert", age: 26, status: "updated" }
)
更新返回结果

updateOne()updateMany()返回操作状态信息(匹配数、修改数),不返回被修改的文档本身。

(四)查:查询文档

1. 基本查询语法

mongodb

复制代码
db.集合.find(<query>,<fields>).sort(key).skip(m).limit(n)
  • <query>:查询条件(等同于 SQL 的 where)
  • <fields>:投影,指定返回 / 隐藏的字段(等同于 SQL 的 select)
  • sort(key):排序,1升序,-1降序
  • skip(m):跳过前 m 条文档
  • limit(n):限制返回 n 条文档
2. 游标特性

执行var result = db.集合.find()时,返回的是游标而非直接返回所有数据:

  • 懒加载:仅遍历(如forEach()toArray())时才从数据库批量获取数据
  • 分批获取:默认每次获取 101 个文档或 1MB 数据(取较小值)
  • 生命周期:创建→遍历→耗尽(一次性,无法回头)→关闭(超时默认 10 分钟)
  • findOne():直接返回第一个符合条件的文档(对象形式),无匹配则返回null
3. 条件查询
(1)比较运算符

表格

操作符 含义 类比 SQL 示例
$eq 等于 = {likenum:{$eq:1000}}
$ne 不等于 {likenum:{$ne:3000}}
$gt 大于 > {likenum:{$gt:2000}}
$gte 大于等于 {likenum:{$gte:1000}}
$lt 小于 < {likenum:{$lt:100}}
$lte 小于等于 {likenum:{$lte:1000}}
$in 在指定数组内 IN {userid:{$in:["1002","1003"]}}
$nin 不在指定数组内 NOT IN {state:{$nin:["0","2"]}}
(2)逻辑运算符

表格

操作符 含义 示例
$and 逻辑与(可省略) {likenum:{$gte:1000},$lte:2000}
$or 逻辑或(数组包裹条件) {$or:[{userid:"1002"},{state:"0"}]}
$not 逻辑非 {likenum:{$not:{$gt:30}}}
$nor 逻辑或非(都不满足) -
(3)其他常用运算符

表格

操作符 含义 示例
$exists 字段是否存在 {score:{$exists:true}}(存在 score 字段)
$regex 正则表达式匹配 {content:{$regex:/水/}}
(4)数组查询

表格

查询方式 语法 说明
完全匹配 {skills: ["Java", "Python"]} 顺序、元素数量必须完全一致
包含查询 {skills: "Java"} 数组包含指定元素即可(最常用)
全包含 {skills:{$all:["Java","Go"]}} 数组同时包含所有指定元素,顺序无关
(5)日期查询

mongodb

复制代码
// 查询2019年8月6日之后发布的评论
db.comment.find({ createdatetime: { $gt: new Date("2019-08-06") } })
4. 分页查询
基础分页(适合小数据量)

mongodb

复制代码
// 公式:skip=(页码-1)*每页条数,limit=每页条数
// 示例:查询articleid=100001的评论,按时间倒序,每页2条,第2页
db.comment.find({ articleid: "100001" })
.sort({ createdatetime: -1 })
.skip(2)
.limit(2);

注意skip(n)当 n 很大时性能下降,需扫描并跳过大量文档。

高效分页(适合大数据量,基于条件)

利用上一页最后一条数据的唯一字段(如_id)作为查询条件,避免skip

mongodb

复制代码
// 示例:上一页最后一条文档_id为"2",查询下一页
db.comment.find({
articleid: "100001",
_id: { $gt: "2" }
})
.sort({ _id: 1 })
.limit(2);
5. 投影(Projection)

核心铁律 :除_id外,不能混用0(隐藏)和1(显示),支持白名单 (只显示)和黑名单(只隐藏)。

白名单模式(只显示指定字段)

mongodb

复制代码
// 显示name和score,隐藏其他(_id默认显示)
db.students.find({}, {name:1, score:1})
// 显示name和score,隐藏_id
db.students.find({}, {name:1, score:1, _id:0})
黑名单模式(隐藏指定字段)

mongodb

复制代码
// 隐藏skills,显示其他所有字段
db.students.find({}, {skills:0})
6. 正则查询

mongodb

复制代码
// 完整语法
db.collection.find({ 字段名: { $regex: /正则表达式/, $options: "匹配选项" } })
// 简写
db.collection.find({ 字段名: /正则表达式/ })
常用示例

mongodb

复制代码
db.comment.find({ content: /^研究表明/ }) // 以"研究表明"开头
db.comment.find({ content: /水|烫/ }) // 包含"水"或"烫"
db.comment.find({ content: /m/i }) // 包含m/M(忽略大小写)
db.comment.find({ content: /烫嘴。$/ }) // 以"烫嘴。"结尾
db.comment.find({ content: { $not: /专家/ } }) // 不包含"专家"
匹配选项
  • i:忽略大小写
  • m:多行模式,^/$匹配每行开头 / 结尾
  • x:忽略正则中的空白字符

七、索引

1. 索引基础

  • 作用:支持高效查询,避免全集合扫描
  • 数据结构:MongoDB 使用B-Tree(MySQL 为 B+Tree)
  • 核心原则:最左前缀原则、等值查询字段放复合索引前

2. 索引类型

表格

索引类型 说明 适用场景
单字段索引 单个字段的升序 / 降序索引 单字段等值、排序查询
复合索引 多个字段的组合索引,按字段顺序排序 多字段联合查询、排序
地理空间索引 包含二维索引、二维球面索引 地理坐标查询(如店铺位置、用户地址)
文本索引 支持字符串内容搜索,自动过滤停止词、提取词干 全文检索场景
哈希索引 对字段值的散列进行索引,值分布随机 基于散列的分片,仅支持等值查询

3. 索引失效场景

  1. 索引字段使用函数 / 运算(如{ $where: "this.age + 1 > 25" }
  2. 正则表达式非前缀匹配(如/张三/,仅/^张三/可利用索引)
  3. 隐式类型转换(如索引字段是数字,查询用字符串{age: "25"}
  4. 使用$not/$ne(视场景而定,可能失效)
  5. 多字段查询未匹配复合索引的最左前缀

4. 索引操作管理

(1)查看索引

返回集合中所有索引的数组:

mongodb

复制代码
db.集合.getIndexes()
# 示例返回结果
[
{
"v": 2, // 索引版本
"key": { "_id": 1 }, // 索引字段及排序方向(1升序,-1降序)
"name": "_id_", // 索引名称(系统自动创建_id索引)
"ns": "articledb.comment" // 数据库.集合
}
]
(2)创建索引

mongodb

复制代码
db.集合.createIndex(keys, options)

keys :索引字段及排序方向,如{userid:1, likenum:-1}options:索引配置,常用如下:

表格

选项 说明
unique 唯一索引,确保字段值唯一,null视为一个值
sparse 稀疏索引,仅包含有该字段的文档
expireAfterSeconds TTL 索引,设置文档过期时间(字段需为 Date 类型)
创建示例

mongodb

复制代码
// 单字段索引
db.comment.createIndex({userid:1})
// 唯一索引
db.users.createIndex({ email: 1 }, { unique: true })
// 复合索引
db.comment.createIndex({articleid: 1, likenum: -1, createdatetime: 1})
// 唯一稀疏索引
db.users.createIndex({ email: 1 }, { unique: true, sparse: true })
// TTL索引(1小时后过期)
db.sessions.createIndex({ createdAt: 1 }, { expireAfterSeconds: 3600 })
(3)移除索引

mongodb

复制代码
// 按索引字段删除
db.集合.dropIndex({字段名:排序方向})
// 按索引名称删除
db.集合.dropIndex("索引名")
# 示例
db.comment.dropIndex({userid:1})
db.comment.dropIndex("userid_1_nickname_-1")
(4)分析查询性能

通过explain()查看查询是否使用索引、扫描方式等:

mongodb

复制代码
db.集合.find(query,options).explain(options)

5. 复合索引核心原则

(1)最左前缀原则

查询条件必须匹配复合索引中最左边的 N 个字段 ,索引才能有效使用。示例:索引{age:1, name:1}

表格

查询条件 是否使用索引 原因
{age:25} 匹配最左前缀 age
{age:25, name:"Charlie"} 匹配所有索引字段,效率最高
{name:"Alice"} 跳过最左前缀 age,全集合扫描
{age:{$gt:20}, name:"Bob"} 匹配最左前缀,范围查询后过滤
(2)等值查询 VS 范围查询

复合索引中,等值查询字段放前面,范围查询字段放后面,提升效率。

  • 推荐:索引{age:1, name:1} + 查询{age:25, name:/^C/}
  • 不推荐:索引{name:1, age:1} + 查询{age:25, name:/^C/}
(3)排序与索引方向

多字段排序时,排序方向需与复合索引方向完全匹配 / 相反 ,才能利用索引。示例:索引{age:1, name:1}

  • 支持:sort({age:1, name:1})sort({age:-1, name:-1})
  • 不支持:sort({age:1, name:-1})

八、聚合

聚合操作用于处理数据记录并返回计算结果(统计、求和、分组等),将多个文档的数值分组计算,返回单个 / 多个结果。

聚合类型

  1. 单一作用聚合:简单的聚合函数,如计数、去重
  2. 聚合管道:核心聚合框架,多阶段流水线处理数据(推荐)
  3. MapReduce:分 map 和 reduce 阶段,适用于复杂聚合场景

(一)单一作用聚合

简单高效,适用于常见的聚合需求,仅作用于单个集合。

常用函数

表格

函数 说明 示例
estimatedDocumentCount() 忽略条件,返回集合文档总数(估算,速度快) db.books.estimatedDocumentCount()
countDocuments() 统计匹配条件的文档数(精确,全表扫描) db.users.countDocuments({age:{$lt:30}})
distinct() 查找指定字段的不同值,返回数组 db.books.distinct("type", {favCount:{$gt:90}})

(二)聚合管道(Aggregation Framework)

MongoDB 聚合的核心,基于流水线概念,文档依次经过多个 ** 阶段(Stage)** 处理,每个阶段的输出作为下一个阶段的输入,最终返回结果。

核心语法

mongodb

复制代码
db.collection.aggregate([
{ $stage1: { <expression> } },
{ $stage2: { <expression> } },
// ... 更多阶段
])
常用管道阶段(Stage)

表格

阶段 SQL 等价 作用 示例
$match WHERE 过滤文档,尽可能放在管道最前面 {$match: {age:{$gt:25}, gender:"male"}}
$project AS 重塑文档结构,选择 / 重命名 / 计算字段 {$project: {_id:0, name:1, isAdult:{$gte:["$age",18]}}}
$sort ORDER BY 对文档排序 {$sort: {age:-1, name:1}}
$limit LIMIT 限制返回文档数量 {$limit:10}
$skip SKIP 跳过指定数量文档 {$skip:5}
$count COUNT 统计管道内文档数,返回单个计数字段 {$count: "total_male_users"}
$addFields - 为文档添加新字段,保留原有字段 {$addFields: {isAdult:{$gte:["$age",18]}}}
$group GROUP BY 按条件分组,执行聚合计算 {$group: {_id:"$userId", total:{$sum:1}}}
$lookup LEFT OUTER JOIN 左连接另一个集合,结果为数组 见下文示例
$unwind - 将数组字段展开为多个文档 见下文示例
关键聚合阶段示例
1. $group 分组聚合

核心语法

mongodb

复制代码
{
$group: {
_id: <分组依据>, // 必选,字段名/表达式/null(全局分组)
<新字段>: { <聚合操作符>: <表达式> }, // 可选,聚合计算
...
}
}

常用聚合操作符

表格

操作符 含义 类比 SQL
$sum 求和 SUM
$avg 计算平均值 AVG
$max 取最大值 MAX
$min 取最小值 MIN
$first 取每组第一个文档 LIMIT 0,1
$last 取每组最后一个文档 -
$push 将字段值加入数组(允许重复) -
$addToSet 将字段值加入集合(无重复) -

分组示例:按用户 ID 统计订单信息

mongodb

复制代码
db.orders.aggregate([
{
$group: {
_id: "$userId",
orderCount: { $sum: 1 }, // 订单数
totalAmount: { $sum: { $multiply: ["$price", "$quantity"] } } // 总消费
}
},
{
$project: {
userId: "$_id",
orderCount: 1,
totalAmount: 1,
_id: 0
}
}
])
2. $lookup 左连接

核心语法

mongodb

复制代码
{
$lookup: {
from: "<目标集合>", // 要连接的集合
localField: "<当前集合匹配字段>", // 本集合的关联字段
foreignField: "<目标集合匹配字段>", // 目标集合的关联字段
as: "<结果数组字段名>" // 存储匹配结果的数组
}
}

示例:订单集合关联产品集合

mongodb

复制代码
// 订单集合orders + 产品集合products
db.orders.aggregate([
{
$lookup: {
from: "products",
localField: "productId",
foreignField: "_id",
as: "productDetails"
}
}
])

特性 :匹配结果为数组,无匹配则返回空数组[]

3. $unwind 数组展开

将数组字段展开为多个文档,每个文档包含数组的一个元素,配合$lookup使用:

mongodb

复制代码
db.orders.aggregate([
{ $lookup: { from: "products", localField: "productId", foreignField: "_id", as: "productDetails" } },
{ $unwind: "$productDetails" } // 展开productDetails数组
])

特性 :若数组为空,$unwind会丢弃该文档。

聚合管道数学操作符

用于数值计算,常配合$project/$addFields/$group使用:

  • $abs:绝对值
  • $add:求和
  • $subtract:求差
  • $multiply:乘积
  • $divide:求商
  • $mod:取余
  • $sqrt:平方根

(三)MapReduce

mapreduce两个阶段,适用于复杂的聚合场景,略(日常开发中聚合管道已满足大部分需求)。

九、findAndModify 原子操作

查找修改 (/ 删除)合并为一个原子操作,适用于基于文档当前状态更新并立即获取结果的场景(如计数器、任务队列)。

核心语法

mongodb

复制代码
db.collection.findAndModify({
query: <document>, // 查询条件
sort: <document>, // 多匹配时排序,仅修改第一个
update: <document>, // 更新操作(与remove互斥)
fields: <document>, // 指定返回字段
new: <boolean>, // 是否返回更新后文档,默认false
upsert: <boolean>, // 无匹配时是否插入,默认false
remove: <boolean> // 是否删除找到的文档,默认false(与update互斥)
});

关键示例

  1. 更新并返回最新文档(库存扣减)

mongodb

复制代码
var result = db.products.findAndModify({
query: { name: "iPhone" },
update: { $inc: { stock: -1 } },
new: true
});
printjson(result); // 返回更新后的文档
  1. 无则加(upsert)

mongodb

复制代码
var result = db.products.findAndModify({
query: { name: "Google Pixel" },
update: {
$setOnInsert: { price: 5499 },
$inc: { stock: 20 }
},
new: true,
upsert: true
});

findAndModify VS updateOne 核心区别

表格

特性 findAndModify updateOne/updateMany
核心功能 查找并原子修改 / 删除一个文档 原子修改一个 / 多个匹配文档
返回结果 返回被修改 / 删除的文档(前 / 后版本) 返回操作状态(匹配数、修改数),不返回文档
文档选择 多匹配时通过 sort 指定修改哪个 多匹配时修改第一个(内部自然顺序,无法指定)
适用场景 1. 更新后需立即获取文档2. 基于当前状态更新3. 计数器 / 任务队列 1. 只需更新,无需获取文档2. 明确指定文档更新3. 批量更新
原子性 操作本身是原子的 操作本身是原子的
相关推荐
白藏y2 小时前
【MySQL】CentOS 7 环境下 MySQL 5.7 完整安装与配置
数据库·mysql
小箌2 小时前
JavaWeb_02
java·数据库·maven·mybatis
数据知道2 小时前
详解MongoDB混合部署策略:复制集与分片集群的组合应用
数据库·mongodb
溜达的大象2 小时前
后端常用技术全方位分析:从核心标配到淘汰弃用,一文理清技术选型逻辑
网络·数据库
麦聪聊数据2 小时前
QuickAPI 如何重塑可视化大屏与 BI 的数据交付链路?
数据库·sql·低代码·微服务·重构
草莓熊Lotso2 小时前
手搓简易 Linux 进程池:从 0 到 1 实现基于管道的任务分发系统
linux·运维·服务器·数据库·c++·人工智能
霖霖总总2 小时前
[Redis小技巧11]Redis Key 过期策略与内存淘汰机制:深度解析与实战指南
数据库·redis
猹叉叉(学习版)2 小时前
【ASP.NET CORE】 9. 托管服务
数据库·笔记·后端·c#·asp.net·.netcore
Francek Chen3 小时前
【大数据存储与管理】分布式数据库HBase:03 HBase数据模型
大数据·数据库·hadoop·分布式·hdfs·hbase