文章目录
前言
在数据的世界里,MongoDB
就像一位灵活多变的魔术师,而它的操作符则是这位魔术师手中最得心应手的魔法棒。想象一下,当你需要在海量数据中快速找到特定信息时,只需要轻轻挥动这根“魔法棒”——也就是 $
加上关键字的语法——就能让复杂的数据查询变得如同翻阅一本清晰有序的笔记本般简单。
MongoDB
的操作符以 $
开头,每一个关键字都承载着独特的功能和使命:从精确匹配到范围筛选,从逻辑组合到数组操作,甚至是复杂的数据聚合与转换,它们为开发者提供了一种强大而直观的语言。这种设计不仅简化了代码书写,还赋予了查询逻辑极高的灵活性和可读性。
例如,当你要查找年龄大于20且状态为“active
”的用户时,无需编写冗长的条件语句,只需一句 { $and: [ { age: { $gt: 20 } }, { status: "active" } ] }
,即可轻松实现目标。又或者,当你需要对一组文档进行分组统计、排序并提取关键字段时,通过 $group
、 $sort
和 $project
等操作符的巧妙搭配,你可以在短短几行代码内完成原本繁琐的工作。
正是这些简洁却强大的 $
操作符,使得 MongoDB
成为了现代开发者的利器。无论是初学者还是资深工程师,都能从中感受到那种化繁为简、事半功倍的便捷体验。接下来,让我们一起深入探索这些神奇的操作符,看看它们如何帮助我们驾驭数据的洪流,创造无限可能!
一、查询操作符
1.1 比较操作符
👉👉👉语法:
- $eq:等于
- $ne:不等于
- $gt:大于
- $gte:大于等于
- $lt:小于
- $lte:小于等于
- $in:值在数组中
- $nin:值不在数组中
🔥🔥🔥示例:
db.products.find({ price: { $eq: 800 } }) //查找价格等于 800 的产品
db.products.find({ price: { $ne: 800 } }) //查找价格不等于 800 的产品
db.products.find({ price: { $gt: 200 } }) //查找价格大于 200 的产品
db.products.find({ price: { $gte: 200 } }) //查找价格大于或等于 200 的产品
db.products.find({ price: { $lt: 200 } }) //查找价格小于 200 的产品
db.products.find({ price: { $lte: 200 } }) //查找价格小于或等于 200 的产品
db.products.find({ price: { $in: [1200, 800] } }) //查找价格为 1200 或 800 的产品
db.products.find({ price: { $nin: [1200, 800] } }) //查找价格既不是 1200 也不是 800 的产品
1.2 逻辑操作符
👉👉👉语法:
- $and:逻辑与
- $or:逻辑或
- $not:逻辑非
- $nor:逻辑或非
🔥🔥🔥示例:
// 查找年龄大于 20 且分数大于 80 的学生
db.students.find({
$and: [
{ age: { $gt: 20 } },
{ score: { $gt: 80 } }
]
})
// 查找年龄小于 25 或分数大于 80 的学生
db.students.find({
$or: [
{ age: { $lt: 25 } },
{ score: { $gt: 80 } }
]
})
// 查找分数不大于 80 的学生(即分数小于或等于 80)
db.students.find({
score: { $not: { $gt: 80 } }
})
// 查找既不是活跃状态,也不是分数大于 80 的学生
db.students.find({
$nor: [
{ active: true },
{ score: { $gt: 80 } }
]
})
1.3 元素操作符
👉👉👉语法:
- $exists:检查字段是否存在
- $type:匹配字段类型
🔥🔥🔥示例:
// 查找包含 email 字段的用户
db.users.find({ email: { $exists: true } })
// 查找不包含 email 字段的用户
db.users.find({ email: { $exists: false } })
// 查找包含某个字段但值为 null 的文档
// 注意:如果字段值为 null 或字段不存在,$exists 都会返回 false。可以通过显式检查 null 来区分
db.users.find({ age: { $exists: true }, age: null })
// 查找 age 字段是字符串类型的用户(例如 "22")
db.users.find({ age: { $type: "string" } })
// 查找 age 字段是数字类型的用户
db.users.find({ age: { $type: "number" } })
// MongoDB 支持通过类型别名(如 "string"、"number")或 BSON 类型编号(如 2 表示字符串,16 表示 32 位整数)来指定类型。
// 查找 age 字段是 32 位整数类型的用户
db.users.find({ age: { $type: 16 } })
// 查找 age 字段是字符串类型的用户
db.users.find({ age: { $type: 2 } })
// 查找 age 字段是字符串或数字类型的用户
db.users.find({ age: { $type: ["string", "number"] } })
1.4 数组操作符
👉👉👉语法:
- $all:数组包含所有指定的值
- $elemMatch:匹配数组中的某个元素满足多个条件
- $size:匹配数组长度
🔥🔥🔥示例:
// 给定mongo数据
{ "_id": 1, "name": "Laptop", "tags": ["electronics", "gaming", "portable"] }
{ "_id": 2, "name": "Smartphone", "tags": ["electronics", "portable"] }
{ "_id": 3, "name": "Headphones", "tags": ["audio", "gaming"] }
{ "_id": 4, "name": "Desk Chair", "tags": ["furniture", "office"] }
{ "_id": 5, "name": "Coffee Mug", "tags": ["kitchenware", "portable"] }
// 查找 tags 数组中同时包含 "electronics" 和 "portable" 的产品
db.products.find({ tags: { $all: ["electronics", "portable"] } })
//给定mongo数据
{ "_id": 1, "name": "Alice", "scores": [85, 90, 78] }
{ "_id": 2, "name": "Bob", "scores": [60, 88, 92] }
{ "_id": 3, "name": "Charlie", "scores": [72, 75, 80] }
// 查找 scores 数组中至少有一个元素大于 80 且小于 90 的学生
db.students.find({
scores: {
$elemMatch: {
$gt: 80,
$lt: 90
}
}
})
// 查找 tags 数组长度为 3 的产品
db.products.find({ tags: { $size: 3 } })
1.5 评估操作符
操作符 | 含义 | 示例查询 | 示例结果 |
---|---|---|---|
$expr | 在查询中使用聚合表达式。 | { $expr: { $gt: ["$price", "$quantity"] } } | 价格大于数量的文档。 |
$jsonSchema | 根据 JSON Schema 验证文档结构。 | { $jsonSchema: { ... } } | 符合指定模式的文档。 |
$mod | 取模运算。 | { value: { $mod: [5, 0] } } | 取模等于 0 的文档。 |
$regex | 正则表达式匹配字符串字段。 | { name: { $regex: "^A" } } | 名字以 “A” 开头的文档。 |
$text | 在启用了全文索引的字段上进行文本搜索。 | { $text: { $search: "basics" } } | 包含单词 “basics” 的文档。 |
$where | 使用 JavaScript 表达式定义复杂条件。 | { $where: "this.age > 28" } | 年龄大于 28 的文档。 |
👉👉👉语法:
- $expr:允许在查询中使用聚合表达式
- $jsonSchema:根据 JSON Schema 验证文档
- $mod:取模运算
- $regex:正则匹配
- $text:文本搜索
- $where:JavaScript 表达式
🔥🔥🔥示例:
// $expr 允许在查询条件中使用聚合框架中的表达式(如 $gt、$sum 等)
// 给定mongo数据
{ "_id": 1, "product": "Laptop", "price": 1200, "quantity": 2 }
{ "_id": 2, "product": "Smartphone", "price": 800, "quantity": 5 }
{ "_id": 3, "product": "Headphones", "price": 150, "quantity": 10 }
// 查找价格大于数量的产品
db.sales.find({
$expr: { $gt: ["$price", "$quantity"] }
})
// $jsonSchema 查找符合以下 JSON Schema 的文档
db.products.find({
$jsonSchema: {
bsonType: "object",
required: ["name", "price"],
properties: {
name: { bsonType: "string" },
price: { bsonType: "number", minimum: 0 }
}
}
})
// $mod 匹配字段值对某个数取模后等于指定值的文档。
// 给定mongo数据
{ "_id": 1, "value": 10 }
{ "_id": 2, "value": 15 }
{ "_id": 3, "value": 20 }
// 查找 value 对 5 取模等于 0 的文档
db.numbers.find({ value: { $mod: [5, 0] } })
// $regex 正则匹配
// 给定文档
{ "_id": 1, "name": "Alice" }
{ "_id": 2, "name": "Bob" }
{ "_id": 3, "name": "Charlie" }
// 查找名字以字母 "A" 开头的用户
db.users.find({ name: { $regex: "^A" } })
// $text 在启用了全文索引的字段上进行文本搜索
// 假设集合 articles 启用了全文索引
{ "_id": 1, "title": "MongoDB Basics", "content": "Learn the basics of MongoDB." }
{ "_id": 2, "title": "Advanced MongoDB", "content": "Deep dive into advanced MongoDB features." }
// 创建全文索引
db.articles.createIndex({ content: "text" })
// 执行文本搜索
db.articles.find({ $text: { $search: "basics" } })
// $where 使用 JavaScript 表达式定义复杂的查询条件
// 给的mongo数据
{ "_id": 1, "name": "Alice", "age": 25 }
{ "_id": 2, "name": "Bob", "age": 30 }
{ "_id": 3, "name": "Charlie", "age": 35 }
// 查找年龄大于 28 的人
db.people.find({ $where: "this.age > 28" })
二、更新操作符
2.1 字段操作符
👉👉👉语法:
- $set:设置字段值
- $unset:删除字段
- $rename:重命名字段
- $inc:增加字段值
- $mul:乘以字段值
- $min:仅当新值小于当前值时更新
- $max:仅当新值大于当前值时更新
- $currentDate:设置字段为当前日期
🔥🔥🔥示例:
// 将 _id 为 1 的文档的 name 字段设置为 "Alice",同时设置 age 字段为 25。如果 age 字段不存在,则会创建它。
db.users.updateOne(
{ _id: 1 },
{ $set: { name: "Alice", age: 25 } }
)
// 将 _id 为 1 的文档中的 age 字段删除。"" 是占位符,实际值无意义,只需指定字段名即可。
db.users.updateOne(
{ _id: 1 },
{ $unset: { age: "" } }
)
// 将 _id 为 1 的文档中的 name 字段重命名为 fullName。
db.users.updateOne(
{ _id: 1 },
{ $rename: { name: "fullName" } }
)
// 将 _id 为 1 的文档中的 age 字段值增加 5。如果 age 字段不存在,则会创建并设置为 5。
db.users.updateOne(
{ _id: 1 },
{ $inc: { age: 5 } }
)
// 将 _id 为 1 的文档中的 salary 字段值乘以 1.5。如果 salary 字段不存在,则会创建并设置为 0。
db.users.updateOne(
{ _id: 1 },
{ $mul: { salary: 1.5 } }
)
// 将 _id 为 1 的文档中的 price 字段更新为 100,但仅当当前值大于 100 时才会更新。
db.products.updateOne(
{ _id: 1 },
{ $min: { price: 100 } }
)
// 将 _id 为 1 的文档中的 stock 字段更新为 50,但仅当当前值小于 50 时才会更新。
db.products.updateOne(
{ _id: 1 },
{ $max: { stock: 50 } }
)
// 将 _id 为 1 的文档中的 lastModified 字段设置为当前日期。true 表示使用日期类型(而不是时间戳)。
db.logs.updateOne(
{ _id: 1 },
{ $currentDate: { lastModified: true } }
)
// 如果需要设置为时间戳
db.logs.updateOne(
{ _id: 1 },
{ $currentDate: { lastModified: { $type: "timestamp" } } }
)
2.2 数组操作符
👉👉👉语法:
- $push:向数组添加元素
- $pop:从数组移除第一个或最后一个元素
- $pull:从数组移除匹配的元素
- $addToSet:向数组添加唯一元素
- $pullAll:从数组移除多个匹配的元素
- $each:与 $push 或 $addToSet 结合使用,批量操作数组
- $position:指定插入位置
- $slice:限制数组大小
🔥🔥🔥示例:
// 1. 使用 $push 向数组添加元素
db.users.updateOne(
{ _id: 1 }, // 查询条件:找到 _id 为 1 的文档
{ $push: { hobbies: "cooking" } } // 将 "cooking" 添加到 hobbies 数组中
);
// 解释:在 hobbies 数组中添加一个新元素 "cooking"。
// 2. 使用 $pop 从数组移除第一个或最后一个元素
db.users.updateOne(
{ _id: 1 },
{ $pop: { hobbies: 1 } } // 移除数组的最后一个元素
);
// 解释:移除 hobbies 数组的最后一个元素。
db.users.updateOne(
{ _id: 1 },
{ $pop: { hobbies: -1 } } // 移除数组的第一个元素
);
// 解释:移除 hobbies 数组的第一个元素。
// 3. 使用 $pull 从数组移除匹配的元素
db.users.updateOne(
{ _id: 1 },
{ $pull: { hobbies: "reading" } } // 移除数组中等于 "reading" 的元素
);
// 解释:从 hobbies 数组中删除所有等于 "reading" 的元素。
// 4. 使用 $addToSet 向数组添加唯一元素
db.users.updateOne(
{ _id: 1 },
{ $addToSet: { hobbies: "swimming" } } // 只有当数组中不存在 "swimming" 时才添加
);
// 解释:向 hobbies 数组中添加 "swimming",但仅当数组中不存在该元素时才会添加。
// 5. 使用 $pullAll 从数组移除多个匹配的元素
db.users.updateOne(
{ _id: 1 },
{ $pullAll: { hobbies: ["swimming", "cooking"] } } // 移除数组中等于 "swimming" 或 "cooking" 的元素
);
// 解释:从 hobbies 数组中删除所有等于 "swimming" 或 "cooking" 的元素。
// 6. 使用 $each 批量操作数组(与 $push 结合使用)
db.users.updateOne(
{ _id: 1 },
{ $push: { hobbies: { $each: ["dancing", "singing"] } } } // 将多个元素批量添加到数组中
);
// 解释:向 hobbies 数组中一次性添加多个元素 "dancing" 和 "singing"。
// 7. 使用 $position 指定插入位置(与 $push 和 $each 结合使用)
db.users.updateOne(
{ _id: 1 },
{
$push: {
hobbies: {
$each: ["gaming"], // 插入的元素
$position: 0 // 插入到数组的第一个位置
}
}
}
);
// 解释:将 "gaming" 插入到 hobbies 数组的第一个位置(索引为 0)。
// 8. 使用 $slice 限制数组大小(与 $push 和 $each 结合使用)
db.users.updateOne(
{ _id: 1 },
{
$push: {
hobbies: {
$each: ["painting", "writing"], // 插入的元素
$slice: -3 // 保留数组的最后 3 个元素
}
}
}
);
// 解释:向 hobbies 数组中添加 "painting" 和 "writing",并保持数组最多包含最后 3 个元素。
三、管道操作符
3.1 输入输出阶段
👉👉👉语法:
- $match:过滤文档
- $project:重塑文档结构
- $group:按字段分组
- $sort:排序
- $limit:限制返回的文档数量
- $skip:跳过指定数量的文档
- $lookup:执行左外连接
- $out:将结果写入集合
- $merge:将结果合并到集合
🔥🔥🔥示例:
// 1. 使用 $match 过滤文档
const pipeline = [
{
$match: { age: { $gt: 25 } } // 筛选年龄大于 25 的用户
},
// 解释:过滤出年龄大于 25 的用户文档。
];
let result = db.users.aggregate(pipeline);
print("Step 1 - $match: Filter users older than 25");
result.forEach(doc => printjson(doc));
// 2. 使用 $project 重塑文档结构
const pipelineProject = [
{
$project: {
_id: 0, // 排除 _id 字段
fullName: "$name", // 将 name 字段重命名为 fullName
isAdult: { $cond: { if: { $gte: ["$age", 18] }, then: true, else: false } } // 添加一个新字段 isAdult
}
}
];
// 解释:重塑文档结构,排除 _id,添加 fullName 和 isAdult 字段。
result = db.users.aggregate(pipelineProject);
print("\nStep 2 - $project: Reshape document structure");
result.forEach(doc => printjson(doc));
// 3. 使用 $group 按字段分组
const pipelineGroup = [
{
$group: {
_id: "$userId", // 按 userId 分组
totalAmount: { $sum: "$amount" } // 计算每个用户的总金额
}
}
];
// 解释:按 userId 分组并计算每个用户的订单总金额。
result = db.orders.aggregate(pipelineGroup);
print("\nStep 3 - $group: Group by userId and calculate total amount");
result.forEach(doc => printjson(doc));
// 4. 使用 $sort 排序
const pipelineSort = [
{
$sort: { age: 1 } // 按年龄升序排序(1 表示升序,-1 表示降序)
}
];
// 解释:按年龄升序排列用户文档。
result = db.users.aggregate(pipelineSort);
print("\nStep 4 - $sort: Sort users by age in ascending order");
result.forEach(doc => printjson(doc));
// 5. 使用 $limit 限制返回的文档数量
const pipelineLimit = [
{
$limit: 2 // 限制只返回前 2 个文档
}
];
// 解释:仅返回前 2 个用户文档。
result = db.users.aggregate(pipelineLimit);
print("\nStep 5 - $limit: Limit to the first 2 documents");
result.forEach(doc => printjson(doc));
// 6. 使用 $skip 跳过指定数量的文档
const pipelineSkip = [
{
$skip: 1 // 跳过第一个文档
}
];
// 解释:跳过第一个文档,从第二个文档开始返回。
result = db.users.aggregate(pipelineSkip);
print("\nStep 6 - $skip: Skip the first document");
result.forEach(doc => printjson(doc));
// 7. 使用 $lookup 执行左外连接
const pipelineLookup = [
{
$lookup: {
from: "orders", // 从 orders 集合中查找数据
localField: "_id", // 当前集合的字段
foreignField: "userId", // orders 集合的字段
as: "userOrders" // 输出结果的字段名
}
}
];
// 解释:将 users 集合与 orders 集合进行左外连接,生成一个新的字段 userOrders。
result = db.users.aggregate(pipelineLookup);
print("\nStep 7 - $lookup: Perform a left outer join with orders");
result.forEach(doc => printjson(doc));
// 8. 使用 $out 将结果写入集合
const pipelineOut = [
{
$match: { age: { $gt: 25 } } // 筛选年龄大于 25 的用户
},
{
$out: "filteredUsers" // 将结果写入 filteredUsers 集合
}
];
// 解释:将筛选后的结果写入一个新的集合 filteredUsers。
db.users.aggregate(pipelineOut);
print("\nStep 8 - $out: Write results to a new collection 'filteredUsers'");
db.filteredUsers.find().forEach(doc => printjson(doc));
// 9. 使用 $merge 将结果合并到集合
const pipelineMerge = [
{
$match: { age: { $gt: 25 } } // 筛选年龄大于 25 的用户
},
{
$merge: {
into: "mergedUsers", // 合并到 mergedUsers 集合
whenMatched: "replace", // 如果匹配到已有文档,则替换
whenNotMatched: "insert" // 如果没有匹配到,则插入
}
}
];
// 解释:将筛选后的结果合并到 mergedUsers 集合。
3.2 表达式操作符
👉👉👉语法:
- 算术表达式:
$add、$subtract、$multiply、$divide、$mod
- 字符串表达式:
$concat、$substr、$toLower、$toUpper
- 日期表达式:
$year、$month、$dayOfMonth、$hour、$minute、$second
- 条件表达式:
$cond、$ifNull
- 数组表达式:
$arrayElemAt、$concatArrays、$filter、$map、$reduce
四、投影操作符
👉👉👉语法:
- $:投影操作符,返回数组中的第一个匹配元素。
- $slice:限制数组返回的元素数量。
- $elemMatch:投影数组中的第一个匹配元素。
五、其他特殊操作符
👉👉👉语法:
- $comment:为查询添加注释
- $meta:访问元数据(如文本得分)
- $redact:根据条件过滤文档内容
- $$ROOT:引用整个文档
- $$CURRENT:引用当前文档
- $$REMOVE:在聚合中移除字段
其他的更加详细的可以参考官网文档。
献给读者
👉👉👉 福利福利💌💌💌私信博主获取更多学习资料和面试资料!
👉👉👉 福利福利💌💌💌私信博主获取更多学习资料和面试资料!
👉👉👉 福利福利💌💌💌私信博主获取更多学习资料和面试资料!
💯面试陪考提供真人一对一简历修改
,知识点巩固
,面试技巧
,面试成功率提升100%
。
💯面试陪考提供真人一对一简历修改
,知识点巩固
,面试技巧
,面试成功率提升100%
。
💯面试陪考提供真人一对一简历修改
,知识点巩固
,面试技巧
,面试成功率提升100%
。
💯 计算机技术的世界浩瀚无垠,充满了无限的可能性和挑战,它不仅是代码与算法的交织,更是梦想与现实的桥梁。无论前方的道路多么崎岖不平,希望你始终能保持那份初心,专注于技术的探索与创新,用每一次的努力和进步书写属于自己的辉煌篇章。
🏰在这个快速发展的数字时代,愿我们都能成为推动科技前行的中坚力量,不忘为何出发,牢记心中那份对技术执着追求的热情。继续前行吧,未来属于那些为之努力奋斗的人们。
亲,码字不易,动动小手,欢迎 点赞 ➕ 收藏,如 🈶 问题请留言(评论),博主看见后一定及时给您答复,💌💌💌
- 🎁 面试一对一🎁
- 🎁 毕业设计🎁
- 🎁 小程序🎁
- 🎁 项目🎁
- 🎁 学习🎁
🎁 任何合作🎁请私信添加微信,欢迎每一个有梦想的股东参与!