Skip to content

查询进阶

本章将介绍MongoDB的高级查询技巧,包括条件操作符、数组查询、正则表达式、嵌套文档查询等内容。

条件操作符

比较操作符

javascript
// 比较操作符

// 准备测试数据
db.products.insertMany([
  { name: "iPhone", price: 6999, stock: 100 },
  { name: "iPad", price: 4999, stock: 50 },
  { name: "MacBook", price: 12999, stock: 30 },
  { name: "AirPods", price: 1299, stock: 200 },
]);

// $eq - 等于
db.products.find({ price: { $eq: 6999 } });
// 等同于
db.products.find({ price: 6999 });

// $ne - 不等于
db.products.find({ price: { $ne: 6999 } });

// $gt - 大于
db.products.find({ price: { $gt: 5000 } });

// $gte - 大于等于
db.products.find({ price: { $gte: 5000 } });

// $lt - 小于
db.products.find({ price: { $lt: 5000 } });

// $lte - 小于等于
db.products.find({ price: { $lte: 5000 } });

// 组合使用
db.products.find({
  price: { $gte: 1000, $lte: 7000 }, // 价格在1000到7000之间
});

// $in - 在列表中
db.products.find({ price: { $in: [1299, 4999] } });

// $nin - 不在列表中
db.products.find({ price: { $nin: [1299, 4999] } });

逻辑操作符

javascript
// 逻辑操作符

// $and - 与操作(所有条件都满足)
db.products.find({
  $and: [{ price: { $gte: 1000 } }, { stock: { $gte: 50 } }],
});
// 简写形式(推荐)
db.products.find({
  price: { $gte: 1000 },
  stock: { $gte: 50 },
});

// $or - 或操作(满足任一条件)
db.products.find({
  $or: [{ price: { $lt: 2000 } }, { stock: { $gt: 100 } }],
});

// $not - 非操作
db.products.find({
  price: { $not: { $gt: 5000 } }, // 价格不大于5000
});

// $nor - 既不...也不...
db.products.find({
  $nor: [{ price: { $lt: 2000 } }, { stock: { $lt: 50 } }],
});
// 价格不小于2000 且 库存不小于50

// 复杂逻辑组合
db.products.find({
  $and: [
    {
      $or: [{ category: "电子" }, { category: "数码" }],
    },
    { price: { $gte: 1000 } },
  ],
});

元素操作符

javascript
// 元素操作符

// $exists - 检查字段是否存在
db.users.find({ email: { $exists: true } }); // 有email字段
db.users.find({ email: { $exists: false } }); // 没有email字段

// $type - 检查字段类型
db.users.find({ age: { $type: "int" } }); // 类型为int
db.users.find({ age: { $type: 16 } }); // 类型码16为int

// 类型名称和编号对应表:
// "double": 1, "string": 2, "object": 3, "array": 4
// "binData": 5, "objectId": 7, "bool": 8, "date": 9
// "null": 10, "regex": 11, "int": 16, "timestamp": 17
// "long": 18, "decimal": 19

数组查询

基本数组查询

javascript
// 数组查询基础

// 准备测试数据
db.products.insertMany([
  { name: "产品A", tags: ["电子", "热销", "新品"], price: 999 },
  { name: "产品B", tags: ["服装", "热销"], price: 199 },
  { name: "产品C", tags: ["电子", "新品"], price: 599 },
  { name: "产品D", tags: ["食品"], price: 29 },
]);

// 精确匹配整个数组
db.products.find({ tags: ["电子", "热销", "新品"] });
// 只匹配tags数组完全等于["电子", "热销", "新品"]的文档

// 匹配数组中包含某个元素
db.products.find({ tags: "电子" });
// 匹配tags数组中包含"电子"的文档

// $in - 匹配数组中包含多个元素之一
db.products.find({ tags: { $in: ["电子", "服装"] } });

// $all - 匹配数组中包含所有指定元素
db.products.find({ tags: { $all: ["电子", "新品"] } });

数组元素操作符

javascript
// $elemMatch - 匹配数组中满足所有条件的元素

// 准备测试数据
db.students.insertMany([
  {
    name: "张三",
    scores: [
      { subject: "数学", score: 90 },
      { subject: "语文", score: 85 },
    ],
  },
  {
    name: "李四",
    scores: [
      { subject: "数学", score: 70 },
      { subject: "语文", score: 95 },
    ],
  },
]);

// 错误写法:分别匹配字段
db.students.find({
  "scores.subject": "数学",
  "scores.score": { $gt: 80 },
});
// 这会匹配到张三和李四,因为条件可以匹配不同的数组元素

// 正确写法:使用$elemMatch
db.students.find({
  scores: {
    $elemMatch: {
      subject: "数学",
      score: { $gt: 80 },
    },
  },
});
// 只匹配数学成绩大于80的学生(张三)

// $size - 匹配数组长度
db.products.find({ tags: { $size: 3 } });
// 匹配tags数组长度为3的文档

数组索引查询

javascript
// 使用索引查询数组元素

// 查询数组第一个元素
db.products.find({ "tags.0": "电子" });

// 查询数组第二个元素
db.products.find({ "tags.1": "热销" });

// $slice - 返回数组的部分元素
db.products.find(
  { name: "产品A" },
  { tags: { $slice: 2 } }, // 只返回前2个元素
);

db.products.find(
  { name: "产品A" },
  { tags: { $slice: -2 } }, // 只返回最后2个元素
);

db.products.find(
  { name: "产品A" },
  { tags: { $slice: [1, 2] } }, // 跳过第1个,返回2个元素
);

// $elemMatch在投影中使用
db.students.find(
  { name: "张三" },
  {
    scores: {
      $elemMatch: { score: { $gte: 90 } },
    },
  },
);
// 只返回分数大于等于90的成绩记录

正则表达式查询

javascript
// 正则表达式查询

// 准备测试数据
db.users.insertMany([
  { name: "张三", email: "zhangsan@example.com" },
  { name: "张四", email: "zhangsi@test.com" },
  { name: "李四", email: "lisi@example.com" },
  { name: "王五", email: "wangwu@example.cn" },
]);

// 使用正则表达式对象
db.users.find({ name: /^/ }); // 姓名以"张"开头
db.users.find({ name: /$/ }); // 姓名以"三"结尾
db.users.find({ name: /|/ }); // 姓名包含"张"或"李"

// 使用$regex操作符
db.users.find({ name: { $regex: /^/ } });

// 忽略大小写
db.users.find({ email: { $regex: /EXAMPLE/i } });

// 使用$regex和$options
db.users.find({
  email: {
    $regex: "example",
    $options: "i", // i: 忽略大小写
  },
});

// 正则与其他条件组合
db.users.find({
  name: { $regex: /^/ },
  age: { $gte: 20 },
});

嵌套文档查询

javascript
// 嵌套文档查询

// 准备测试数据
db.users.insertMany([
  {
    name: "张三",
    address: {
      city: "北京",
      district: "朝阳区",
      street: "朝阳路",
    },
  },
  {
    name: "李四",
    address: {
      city: "上海",
      district: "浦东新区",
      street: "浦东大道",
    },
  },
]);

// 使用点表示法查询嵌套字段
db.users.find({ "address.city": "北京" });
db.users.find({ "address.district": "朝阳区" });

// 嵌套字段条件组合
db.users.find({
  "address.city": "北京",
  "address.district": "朝阳区",
});

// 精确匹配整个嵌套文档(字段顺序必须一致)
db.users.find({
  address: {
    city: "北京",
    district: "朝阳区",
    street: "朝阳路",
  },
});

查询结果处理

排序与分页

javascript
// 排序
db.users.find().sort({ age: 1 }); // 按age升序
db.users.find().sort({ age: -1 }); // 按age降序
db.users.find().sort({ city: 1, age: -1 }); // 多字段排序

// 分页查询
var page = 1;
var pageSize = 10;

db.users
  .find()
  .skip((page - 1) * pageSize)
  .limit(pageSize)
  .sort({ createdAt: -1 });

// 深分页优化(使用范围查询代替skip)
var lastId = ObjectId("...");
db.users.find({ _id: { $gt: lastId } }).limit(10);

投影

javascript
// 投影 - 指定返回字段

// 只返回指定字段
db.users.find({}, { name: 1, email: 1 });

// 排除指定字段
db.users.find({}, { password: 0, salt: 0 });

// 混合使用(不能同时使用包含和排除,除了_id)
db.users.find({}, { name: 1, email: 1, _id: 0 });

// 嵌套字段投影
db.users.find({}, { "address.city": 1 });

// 数组元素投影
db.users.find({}, { tags: { $slice: 3 } });

游标操作

javascript
// 游标操作

var cursor = db.users.find({ age: { $gte: 20 } });

// 检查是否有下一条
cursor.hasNext();

// 获取下一条
cursor.next();

// 遍历游标
cursor.forEach(function (doc) {
  print(doc.name);
});

// 转换为数组
var docs = cursor.toArray();

// 获取查询计划
cursor.explain("executionStats");

本章小结

本章介绍了MongoDB的高级查询技巧:

  1. 条件操作符:掌握比较、逻辑、元素操作符的使用
  2. 数组查询:学会数组元素匹配、$elemMatch、$size等操作
  3. 正则表达式:掌握正则表达式查询方法
  4. 嵌套文档查询:学会使用点表示法查询嵌套字段
  5. 查询结果处理:掌握排序、分页、投影等操作
  6. 游标操作:学会使用游标遍历查询结果

下一章,我们将学习索引,了解如何提高查询性能。