mongodb高阶语法-mongodb七天从入门到精通系列【三】


在这里插入图片描述
在这里插入图片描述


前言

在数据的世界里,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%


💯 计算机技术的世界浩瀚无垠,充满了无限的可能性和挑战,它不仅是代码与算法的交织,更是梦想与现实的桥梁。无论前方的道路多么崎岖不平,希望你始终能保持那份初心,专注于技术的探索与创新,用每一次的努力和进步书写属于自己的辉煌篇章。

🏰在这个快速发展的数字时代,愿我们都能成为推动科技前行的中坚力量,不忘为何出发,牢记心中那份对技术执着追求的热情。继续前行吧,未来属于那些为之努力奋斗的人们。


亲,码字不易,动动小手,欢迎 点赞 ➕ 收藏,如 🈶 问题请留言(评论),博主看见后一定及时给您答复,💌💌💌

  • 🎁 面试一对一🎁
  • 🎁 毕业设计🎁
  • 🎁 小程序🎁
  • 🎁 项目🎁
  • 🎁 学习🎁

🎁 任何合作🎁请私信添加微信,欢迎每一个有梦想的股东参与!


评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小禾科技

感谢来自原始股东的奖励

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值