MongoDB 查询语法基础:操作符详解与应用

想象一下,你已经在 MongoDB 中存储了大量数据,现在需要从中快速准确地找到你需要的信息。就像在图书馆中寻找特定书籍一样,你需要知道如何使用各种"检索工具"来精确定位数据。

MongoDB 的查询语法就是这些强大的检索工具。从简单的精确匹配到复杂的条件组合,从数值比较到文本搜索,掌握查询操作符是高效数据检索的基础。

今天,我们将深入探讨 MongoDB 的核心查询操作符,通过丰富的实例和实际应用场景,让你能够灵活运用各种查询技巧,成为数据检索的高手。

目录

  1. 为什么查询语法如此重要?
  2. MongoDB 查询基础架构
  3. 比较操作符:精确控制数据筛选
  4. 逻辑操作符:复杂条件组合
  5. 数组操作符:处理集合数据
  6. 字符串操作符:文本处理利器
  7. 嵌套文档查询:深入数据结构
  8. 实际应用场景
  9. 常见问题与解决方案
  10. 总结:掌握查询基础

为什么查询语法如此重要?

查询是数据库的核心功能

在数据库的世界里,存储数据只是第一步,检索数据才是核心。MongoDB 的查询语法决定了你能够:

  • 快速定位:从海量数据中精确找到目标记录
  • 灵活筛选:根据复杂条件组合进行数据过滤
  • 高效分析:支持统计、聚合等高级数据分析操作
  • 性能优化:通过合理的查询设计提升系统性能

MongoDB vs SQL 查询对比

让我们先看看 MongoDB 和 SQL 在查询语法上的对应关系:

查询类型SQLMongoDB说明
精确匹配WHERE field = 'value'{field: 'value'}简化的精确匹配语法
数值比较WHERE age > 18{age: {$gt: 18}}使用操作符进行比较
范围查询WHERE id IN (1,2,3){id: {$in: [1,2,3]}}数组包含查询
逻辑组合WHERE age > 18 AND status = 'active'{$and: [{age: {$gt: 18}}, {status: 'active'}]}逻辑操作符组合
模糊查询WHERE name LIKE '%张%'{name: {$regex: /张/, $options: 'i'}}正则表达式查询
空值检查WHERE field IS NULL{field: {$exists: false}}存在性检查

MongoDB 查询的优势:

  • JSON 原生:查询条件本身就是 JSON 格式,更直观
  • 嵌套支持:天然支持嵌套文档和数组查询
  • 灵活扩展:操作符可以灵活组合,适应复杂需求

MongoDB 查询基础架构

查询语句的基本结构

// MongoDB Shell / Node.js
db.collection.find(
  {
    /* 查询条件 */
  }, // filter:筛选条件
  {
    /* 投影选项 */
  } // projection:返回字段控制
);

查询条件(Filter): 决定哪些文档会被返回

投影选项(Projection): 决定返回文档的哪些字段

基础查询示例

// MongoDB Shell / Node.js
// 1. 查询所有文档
db.users.find();

// 2. 带条件的查询
db.users.find({ age: 25 });

// 3. 带投影的查询
db.users.find(
  { age: { $gte: 18 } }, // 查询条件:年龄大于等于18
  { username: 1, email: 1, _id: 0 } // 投影:只返回用户名和邮箱
);

// 4. 限制返回数量
db.products.find({ category: "电子产品" }).limit(10);

// 5. 排序结果
db.articles.find({ status: "published" }).sort({ publishedAt: -1 });

比较操作符:精确控制数据筛选

比较操作符是 MongoDB 查询的基础,用于精确控制数据的筛选条件。

相等性查询:$eq 操作符

基础用法:

// MongoDB Shell / Node.js
// 显式使用 $eq(通常可以省略)
db.users.find({ age: { $eq: 25 } });

// 简写形式(推荐)
db.users.find({ age: 25 });

// 字符串匹配
db.users.find({ status: "active" });

// 布尔值匹配
db.users.find({ isVerified: true });

// null 值匹配
db.users.find({ phone: null });

实际应用示例:

// 1. 用户状态查询
db.users.find({ status: "active" });

// 2. 产品分类查询
db.products.find({ category: "电子产品" });

// 3. 订单状态查询
db.orders.find({ orderStatus: "completed" });

// 4. 多字段精确匹配
db.users.find({
  username: "zhangsan",
  status: "active",
});

数值比较:$gt, $gte, $lt, $lte

操作符说明:

  • $gt:大于(greater than)
  • $gte:大于等于(greater than or equal)
  • $lt:小于(less than)
  • $lte:小于等于(less than or equal)

基础用法:

// MongoDB Shell / Node.js
// 1. 大于比较
db.users.find({ age: { $gt: 18 } }); // 年龄大于18
db.products.find({ price: { $gt: 100 } }); // 价格大于100

// 2. 大于等于比较
db.users.find({ age: { $gte: 18 } }); // 年龄大于等于18
db.orders.find({ total: { $gte: 1000 } }); // 订单总额大于等于1000

// 3. 小于比较
db.users.find({ age: { $lt: 65 } }); // 年龄小于65
db.products.find({ stock: { $lt: 10 } }); // 库存小于10

// 4. 小于等于比较
db.users.find({ age: { $lte: 65 } }); // 年龄小于等于65
db.products.find({ rating: { $lte: 4.5 } }); // 评分小于等于4.5

实际应用场景:

// 1. 电商价格筛选
db.products.find({
  price: { $gte: 100, $lte: 1000 }, // 价格在100-1000之间
});

// 2. 用户年龄分组
db.users.find({
  age: { $gte: 18, $lt: 30 }, // 18-29岁用户
});

// 3. 库存预警
db.products.find({
  stock: { $lt: 10 }, // 库存小于10
  status: "active", // 且状态为活跃
});

// 4. 评分筛选
db.products.find({
  rating: { $gte: 4.0 }, // 评分大于等于4.0
  reviewCount: { $gte: 100 }, // 且评论数大于等于100
});

// 5. 日期范围查询
db.orders.find({
  createdAt: {
    $gte: new Date("2024-01-01"),
    $lt: new Date("2024-02-01"),
  },
});

范围查询:$in 和 $nin

$in 操作符: 匹配数组中任意一个值

$nin 操作符: 不匹配数组中的任何值

// MongoDB Shell / Node.js
// 1. $in 操作符使用
db.users.find({
  role: { $in: ["admin", "moderator"] },
});

db.products.find({
  category: { $in: ["电子产品", "服装", "图书"] },
});

db.orders.find({
  status: { $in: ["pending", "processing"] },
});

// 2. $nin 操作符使用
db.users.find({
  status: { $nin: ["banned", "deleted"] },
});

db.products.find({
  category: { $nin: ["已下架", "测试商品"] },
});

// 3. 数值范围查询
db.users.find({
  age: { $in: [18, 25, 30, 35] },
});

db.products.find({
  price: { $in: [99, 199, 299, 399] },
});

实际应用示例:

// 1. 多状态查询
db.orders.find({
  status: { $in: ["pending", "confirmed", "shipped"] },
});

// 2. 排除特定用户
db.comments.find({
  userId: { $nin: ["spam_user1", "spam_user2"] },
});

// 3. 特定地区用户
db.users.find({
  "address.province": { $in: ["北京", "上海", "广东"] },
});

// 4. 特定价格点商品
db.products.find({
  price: { $in: [99, 199, 299, 499, 999] },
  status: "active",
});

存在性检查:$exists

$exists 操作符: 检查字段是否存在

// MongoDB Shell / Node.js
// 1. 字段存在检查
db.users.find({ phone: { $exists: true } }); // 有手机号的用户
db.users.find({ avatar: { $exists: true } }); // 有头像的用户

// 2. 字段不存在检查
db.users.find({ phone: { $exists: false } }); // 没有手机号的用户
db.users.find({ avatar: { $exists: false } }); // 没有头像的用户

// 3. 结合其他条件
db.users.find({
  phone: { $exists: true, $ne: null }, // 有手机号且不为null
  status: "active",
});

db.products.find({
  description: { $exists: true, $ne: "" }, // 有描述且不为空
  price: { $gte: 100 },
});

实际应用场景:

// 1. 数据完整性检查
db.users.find({
  email: { $exists: false }, // 找出缺少邮箱的用户
});

// 2. 可选字段查询
db.products.find({
  tags: { $exists: true }, // 有标签的产品
  category: "电子产品",
});

// 3. 用户资料完整性
db.users.find({
  $and: [
    { profile: { $exists: true } },
    { "profile.bio": { $exists: true } },
    { "profile.avatar": { $exists: true } },
  ],
});

逻辑操作符:复杂条件组合

当简单的比较操作符无法满足复杂查询需求时,逻辑操作符就能派上用场了。

AND 逻辑:$and 操作符

$and 操作符: 所有条件都必须满足

// MongoDB Shell / Node.js
// 1. 显式使用 $and
db.users.find({
  $and: [{ age: { $gte: 18 } }, { status: "active" }, { role: "user" }],
});

// 2. 隐式 AND(推荐,更简洁)
db.users.find({
  age: { $gte: 18 },
  status: "active",
  role: "user",
});

// 3. 同一字段的多个条件(必须使用 $and)
db.products.find({
  $and: [{ price: { $gte: 100 } }, { price: { $lte: 1000 } }],
});

实际应用示例:

// 1. 复杂用户筛选
db.users.find({
  age: { $gte: 18, $lte: 65 }, // 年龄范围
  status: "active", // 状态活跃
  "address.city": "北京", // 城市
  createdAt: { $gte: new Date("2023-01-01") }, // 注册时间
});

// 2. 产品多条件筛选
db.products.find({
  category: "电子产品",
  price: { $gte: 100, $lte: 5000 },
  stock: { $gt: 0 },
  rating: { $gte: 4.0 },
  status: "active",
});

// 3. 订单状态查询
db.orders.find({
  status: "completed",
  total: { $gte: 1000 },
  "customer.vipLevel": { $gte: 2 },
  createdAt: {
    $gte: new Date("2024-01-01"),
    $lt: new Date("2024-02-01"),
  },
});

OR 逻辑:$or 操作符

$or 操作符: 满足任意一个条件即可

// MongoDB Shell / Node.js
// 1. 基础 OR 查询
db.users.find({
  $or: [{ role: "admin" }, { role: "moderator" }],
});

// 2. 多字段 OR 查询
db.users.find({
  $or: [{ username: "admin" }, { email: "admin@example.com" }],
});

// 3. 复杂 OR 条件
db.products.find({
  $or: [
    { category: "电子产品", price: { $lt: 500 } },
    { category: "服装", price: { $lt: 200 } },
    { rating: { $gte: 4.5 } },
  ],
});

实际应用场景:

// 1. 权限查询
db.users.find({
  $or: [
    { role: "admin" },
    { role: "super_admin" },
    { permissions: { $in: ["read", "write"] } },
  ],
});

// 2. 搜索功能
db.articles.find({
  $or: [
    { title: { $regex: "MongoDB", $options: "i" } },
    { content: { $regex: "MongoDB", $options: "i" } },
    { tags: { $in: ["MongoDB", "数据库"] } },
  ],
});

// 3. 多状态查询
db.orders.find({
  status: { $in: ["pending", "confirmed", "shipped"] },
  $or: [{ priority: "high" }, { total: { $gte: 1000 } }],
});

NOT 逻辑:$not 和 $nor

$not 操作符: 不满足指定条件

$nor 操作符: 都不满足指定条件

// MongoDB Shell / Node.js
// 1. $not 操作符
db.users.find({
  age: { $not: { $lt: 18 } }, // 年龄不小于18(即大于等于18)
});

db.products.find({
  price: { $not: { $gte: 1000 } }, // 价格不大于等于1000(即小于1000)
});

// 2. $nor 操作符
db.users.find({
  $nor: [{ status: "banned" }, { status: "deleted" }, { role: "guest" }],
});

db.products.find({
  $nor: [{ stock: 0 }, { status: "discontinued" }, { price: { $lt: 10 } }],
});

实际应用示例:

// 1. 排除特定用户
db.comments.find({
  $nor: [
    { userId: { $in: ["spam_user1", "spam_user2"] } },
    { content: { $regex: "广告|推广", $options: "i" } },
  ],
});

// 2. 有效商品查询
db.products.find({
  $nor: [
    { stock: { $lte: 0 } },
    { status: { $in: ["discontinued", "out_of_stock"] } },
    { price: { $lt: 1 } },
  ],
});

// 3. 复杂排除逻辑
db.users.find({
  status: "active",
  $nor: [
    { age: { $lt: 18 } },
    { age: { $gt: 65 } },
    { "address.country": { $ne: "中国" } },
  ],
});

数组操作符:处理集合数据

MongoDB 中的数组是强大的数据结构,数组操作符让我们能够灵活地查询和处理数组数据。

数组元素匹配:$all 和 $elemMatch

$all 操作符: 数组包含所有指定元素

$elemMatch 操作符: 数组元素满足所有指定条件

// MongoDB Shell / Node.js
// 1. $all 操作符
db.articles.find({
  tags: { $all: ["MongoDB", "数据库"] }, // 标签同时包含 MongoDB 和 数据库
});

db.users.find({
  skills: { $all: ["JavaScript", "Node.js"] }, // 技能同时包含 JavaScript 和 Node.js
});

// 2. $elemMatch 操作符
db.orders.find({
  items: {
    $elemMatch: {
      category: "电子产品",
      quantity: { $gte: 2 },
    },
  },
});

db.users.find({
  addresses: {
    $elemMatch: {
      city: "北京",
      isDefault: true,
    },
  },
});

实际应用场景:

// 1. 文章标签组合查询
db.articles.find({
  tags: { $all: ["技术", "教程"] },
  status: "published",
});

// 2. 用户技能匹配
db.users.find({
  skills: { $all: ["Python", "数据分析"] },
  experience: { $gte: 3 },
});

// 3. 订单商品查询
db.orders.find({
  items: {
    $elemMatch: {
      category: "电子产品",
      price: { $gte: 1000 },
      quantity: { $gte: 1 },
    },
  },
});

// 4. 嵌套数组查询
db.articles.find({
  comments: {
    $elemMatch: {
      rating: { $gte: 4 },
      "author.level": "expert",
    },
  },
});

数组大小控制:$size

$size 操作符: 匹配指定长度的数组

// MongoDB Shell / Node.js
// 1. 精确长度匹配
db.articles.find({
  tags: { $size: 3 }, // 标签数组长度为3
});

db.users.find({
  addresses: { $size: 1 }, // 只有一个地址
});

// 2. 空数组检查
db.products.find({
  images: { $size: 0 }, // 没有图片
});

db.users.find({
  orders: { $size: 0 }, // 没有订单记录
});

// 3. 实际应用场景
// 注意:$size 不支持范围查询,下面的写法是错误的
// db.articles.find({ tags: { $size: { $gte: 2 } } })  // ❌ 错误语法

// 替代方案:使用 $expr 进行范围查询
db.articles.find({
  $expr: { $gte: [{ $size: "$tags" }, 2] }, // 至少2个标签
});

$size 的局限性:

// ❌ $size 不支持范围查询
db.articles.find({
  tags: { $size: { $gte: 2 } }, // 错误语法
});

// ✅ 使用 $expr 实现范围查询
db.articles.find({
  $expr: {
    $and: [
      { $gte: [{ $size: "$tags" }, 2] },
      { $lte: [{ $size: "$tags" }, 5] },
    ],
  },
});

字符串操作符:文本处理利器

字符串操作符让我们能够进行灵活的文本查询和匹配。

正则表达式:$regex

$regex 操作符: 使用正则表达式进行模式匹配

// MongoDB Shell / Node.js
// 1. 基础正则查询
db.users.find({
  username: { $regex: /^张/, $options: "i" }, // 用户名以"张"开头,忽略大小写
});

db.articles.find({
  title: { $regex: "MongoDB", $options: "i" }, // 标题包含"MongoDB"
});

// 2. 常用正则模式
db.users.find({
  email: { $regex: /@gmail\.com$/, $options: "i" }, // Gmail邮箱
});

db.products.find({
  name: { $regex: /iPhone|iPad/, $options: "i" }, // 包含iPhone或iPad
});

db.users.find({
  phone: { $regex: /^1[3-9]\d{9}$/ }, // 中国手机号格式
});

正则表达式选项:

  • i:忽略大小写
  • m:多行模式
  • x:忽略空白字符
  • s:单行模式
// 实际应用示例
// 1. 邮箱域名查询
db.users.find({
  email: { $regex: /@(gmail|qq|163)\.com$/, $options: "i" },
});

// 2. 用户名格式验证
db.users.find({
  username: { $regex: /^[a-zA-Z0-9_]{3,20}$/ }, // 3-20位字母数字下划线
});

// 3. 内容关键词搜索
db.articles.find({
  $or: [
    { title: { $regex: "MongoDB|数据库", $options: "i" } },
    { content: { $regex: "MongoDB|数据库", $options: "i" } },
  ],
});

// 4. 价格格式查询
db.products.find({
  priceDisplay: { $regex: /^\$?\d+\.\d{2}$/ }, // 价格格式如 $99.99
});

文本搜索:$text

$text 操作符: 全文搜索(需要创建文本索引)

// MongoDB Shell / Node.js
// 1. 创建文本索引
db.articles.createIndex({
  title: "text",
  content: "text",
  tags: "text",
});

// 2. 基础文本搜索
db.articles.find({
  $text: { $search: "MongoDB 数据库" },
});

// 3. 排除特定词汇
db.articles.find({
  $text: {
    $search: "MongoDB -教程", // 搜索包含MongoDB但不包含教程的文章
    $caseSensitive: false,
  },
});

// 4. 短语搜索
db.articles.find({
  $text: {
    $search: '"MongoDB 教程"', // 精确短语搜索
    $caseSensitive: false,
  },
});

// 5. 文本搜索评分
db.articles
  .find({ $text: { $search: "数据库优化" } }, { score: { $meta: "textScore" } })
  .sort({ score: { $meta: "textScore" } });

文本索引配置:

// 1. 权重配置
db.articles.createIndex(
  {
    title: "text",
    content: "text",
  },
  {
    weights: {
      title: 10, // 标题权重更高
      content: 1,
    },
  }
);

// 2. 语言配置
db.articles.createIndex(
  {
    title: "text",
    content: "text",
  },
  {
    default_language: "chinese", // 中文分词
  }
);

// 3. 实际应用
db.articles
  .find({
    $text: { $search: "性能优化" },
    status: "published",
  })
  .sort({ score: { $meta: "textScore" } });

嵌套文档查询:深入数据结构

MongoDB 支持嵌套文档,查询时需要使用点号表示法来访问嵌套字段。

点号表示法查询

// MongoDB Shell / Node.js
// 1. 单层嵌套查询
db.users.find({
  "address.city": "北京",
});

db.products.find({
  "specifications.color": "黑色",
});

// 2. 多层嵌套查询
db.users.find({
  "profile.social.wechat": { $exists: true },
});

db.orders.find({
  "customer.address.province": "广东",
});

// 3. 数组中的嵌套文档
db.users.find({
  "addresses.city": "上海", // addresses是数组,查询数组中元素的city字段
});

db.products.find({
  "reviews.rating": { $gte: 4 }, // 查询评分大于等于4的评论
});

嵌套文档的复杂查询

// 1. 嵌套文档的多个条件
db.users.find({
  "address.city": "北京",
  "address.district": "朝阳区",
  "address.isDefault": true,
});

// 2. 结合数组操作符
db.orders.find({
  "items.product.category": "电子产品",
  "items.quantity": { $gte: 2 },
});

// 3. 嵌套文档的存在性检查
db.users.find({
  "profile.avatar": { $exists: true },
  "profile.bio": { $exists: true },
});

// 4. 实际应用场景
db.articles.find({
  "author.profile.level": "expert",
  tags: { $in: ["MongoDB", "数据库"] },
  "metadata.views": { $gte: 1000 },
});

实际应用场景

掌握了各种查询操作符后,让我们看看如何在实际业务场景中应用它们。

场景 1:电商商品筛选系统

// Node.js 示例 - 商品筛选API
async function searchProducts(filters) {
  const query = {};

  // 价格范围筛选
  if (filters.minPrice || filters.maxPrice) {
    query.price = {};
    if (filters.minPrice) query.price.$gte = filters.minPrice;
    if (filters.maxPrice) query.price.$lte = filters.maxPrice;
  }

  // 分类筛选
  if (filters.categories && filters.categories.length > 0) {
    query.category = { $in: filters.categories };
  }

  // 品牌筛选
  if (filters.brands && filters.brands.length > 0) {
    query.brand = { $in: filters.brands };
  }

  // 评分筛选
  if (filters.minRating) {
    query.rating = { $gte: filters.minRating };
  }

  // 库存筛选
  if (filters.inStock) {
    query.stock = { $gt: 0 };
  }

  // 状态筛选
  query.status = "active";

  // 关键词搜索
  if (filters.keyword) {
    query.$or = [
      { name: { $regex: filters.keyword, $options: "i" } },
      { description: { $regex: filters.keyword, $options: "i" } },
      { tags: { $in: [filters.keyword] } },
    ];
  }

  const products = await db.products
    .find(query)
    .sort({
      [filters.sortBy || "createdAt"]: filters.sortOrder === "asc" ? 1 : -1,
    })
    .limit(filters.limit || 20)
    .skip((filters.page - 1) * (filters.limit || 20))
    .toArray();

  return products;
}

场景 2:用户管理系统

// Node.js 示例 - 用户查询系统
async function searchUsers(searchParams) {
  const query = {};

  // 基础筛选
  if (searchParams.status) {
    query.status = searchParams.status;
  }

  if (searchParams.role) {
    query.role = { $in: searchParams.role };
  }

  // 年龄范围
  if (searchParams.ageRange) {
    query.age = {};
    if (searchParams.ageRange.min) query.age.$gte = searchParams.ageRange.min;
    if (searchParams.ageRange.max) query.age.$lte = searchParams.ageRange.max;
  }

  // 地理位置筛选
  if (searchParams.city) {
    query["address.city"] = searchParams.city;
  }

  if (searchParams.province) {
    query["address.province"] = searchParams.province;
  }

  // 注册时间范围
  if (searchParams.registeredAfter) {
    query.createdAt = { $gte: new Date(searchParams.registeredAfter) };
  }

  // 用户资料完整性
  if (searchParams.hasProfile) {
    query.$and = [
      { "profile.name": { $exists: true } },
      { "profile.avatar": { $exists: true } },
    ];
  }

  // 搜索关键词
  if (searchParams.keyword) {
    query.$or = [
      { username: { $regex: searchParams.keyword, $options: "i" } },
      { email: { $regex: searchParams.keyword, $options: "i" } },
      { "profile.name": { $regex: searchParams.keyword, $options: "i" } },
    ];
  }

  const users = await db.users
    .find(query)
    .sort({ createdAt: -1 })
    .limit(searchParams.limit || 50)
    .toArray();

  return users;
}

场景 3:内容管理系统

// Node.js 示例 - 文章内容查询
async function searchArticles(searchOptions) {
  const query = {};

  // 状态筛选
  query.status = "published";

  // 分类筛选
  if (searchOptions.category) {
    query.category = searchOptions.category;
  }

  // 标签筛选
  if (searchOptions.tags && searchOptions.tags.length > 0) {
    query.tags = { $in: searchOptions.tags };
  }

  // 作者筛选
  if (searchOptions.author) {
    query["author.username"] = searchOptions.author;
  }

  // 发布时间范围
  if (searchOptions.publishedAfter) {
    query.publishedAt = { $gte: new Date(searchOptions.publishedAfter) };
  }

  // 热门文章筛选
  if (searchOptions.isPopular) {
    query.$or = [
      { "stats.views": { $gte: 1000 } },
      { "stats.likes": { $gte: 100 } },
    ];
  }

  // 全文搜索
  if (searchOptions.searchText) {
    query.$text = { $search: searchOptions.searchText };
  }

  // 构建排序
  let sortOptions = {};
  if (searchOptions.sortBy) {
    switch (searchOptions.sortBy) {
      case "popular":
        sortOptions = { "stats.views": -1 };
        break;
      case "recent":
        sortOptions = { publishedAt: -1 };
        break;
      case "trending":
        sortOptions = { "stats.likes": -1 };
        break;
      default:
        sortOptions = { publishedAt: -1 };
    }
  }

  const articles = await db.articles
    .find(query)
    .sort(sortOptions)
    .limit(searchOptions.limit || 20)
    .toArray();

  return articles;
}

常见问题与解决方案

问题 1:查询性能慢

原因分析:

  • 缺少合适的索引
  • 查询条件不够精确
  • 返回数据量过大

解决方案:

// 1. 创建合适的索引
db.users.createIndex({ email: 1 });
db.products.createIndex({ category: 1, price: -1 });
db.articles.createIndex({ status: 1, publishedAt: -1 });

// 2. 使用投影减少数据传输
db.users.find(
  { status: "active" },
  { username: 1, email: 1, _id: 0 } // 只返回需要的字段
);

// 3. 使用 limit 限制结果数量
db.products.find({ category: "电子产品" }).limit(100);

// 4. 使用 explain 分析查询性能
db.users.find({ email: "test@example.com" }).explain("executionStats");

问题 2:正则表达式性能问题

问题: 正则表达式查询可能导致全表扫描

解决方案:

// ❌ 不好的做法:前缀正则查询
db.users.find({ username: { $regex: /^张/ } });

// ✅ 好的做法:使用文本索引
db.users.createIndex({ username: "text" });
db.users.find({ $text: { $search: "张" } });

// ✅ 或者使用前缀索引
db.users.createIndex({ username: 1 });
db.users.find({ username: { $regex: /^张/ } }); // 现在可以使用索引

问题 3:数组查询的陷阱

问题: 数组查询可能返回意外结果

// 数据示例
{
  _id: 1,
  tags: ["MongoDB", "数据库", "NoSQL"],
  categories: ["技术", "教程"]
}

// 查询:想要同时包含 "MongoDB" 和 "教程"
// ❌ 错误的理解:这样会匹配同时满足两个条件的文档
db.articles.find({
  tags: "MongoDB",
  categories: "教程"
});

// ✅ 正确的做法:如果想要在同一个数组中同时包含,使用 $all
db.articles.find({
  tags: { $all: ["MongoDB", "教程"] }
});

// 或者,如果想要在同一个数组中同时包含多个元素
db.articles.find({
  tags: { $all: ["MongoDB", "教程"] }
});

问题 4:null 值查询的困惑

// 数据示例
{ name: "张三", phone: null }
{ name: "李四" }  // 没有 phone 字段

// 查询包含 null 值的文档
db.users.find({ phone: null });  // 会匹配两种情况

// 只查询 phone 字段存在且为 null 的文档
db.users.find({ phone: { $eq: null } });

// 查询 phone 字段不存在的文档
db.users.find({ phone: { $exists: false } });

// 查询 phone 字段存在且不为 null 的文档
db.users.find({
  phone: { $exists: true, $ne: null }
});

总结:掌握查询基础

通过本文的学习,我们已经掌握了 MongoDB 查询语法的核心操作符:

核心操作符回顾

  1. 比较操作符$eq, $gt, $gte, $lt, $lte, $in, $nin, $exists
  2. 逻辑操作符$and, $or, $not, $nor
  3. 数组操作符$all, $elemMatch, $size
  4. 字符串操作符$regex, $text
  5. 嵌套查询:点号表示法访问嵌套字段

最佳实践要点

  1. 索引优化:为常用查询字段创建合适的索引
  2. 查询精确性:尽量使用精确的查询条件,避免全表扫描
  3. 投影使用:只返回需要的字段,减少网络传输
  4. 结果限制:使用 limit() 控制返回数据量
  5. 性能监控:使用 explain() 分析查询性能

适用场景建议

  • 精确匹配:使用相等性查询和 $in 操作符
  • 范围查询:使用比较操作符组合
  • 复杂条件:使用逻辑操作符组合多个条件
  • 数组处理:使用 $all, $elemMatch 处理数组数据
  • 文本搜索:使用 $regex$text 进行文本匹配
  • 嵌套查询:使用点号表示法访问嵌套字段

下一步学习路径

掌握了查询语法基础后,建议继续学习:

  1. 高级查询技巧:分页查询、游标查询、聚合查询
  2. 查询性能优化:索引策略、执行计划分析
  3. 复杂业务场景:多表关联、数据统计、报表生成

企业级开发注意事项

查询性能优化策略

在企业环境中,查询性能直接影响用户体验和系统稳定性:

1. 索引策略管理

// 为常用查询字段创建索引
db.users.createIndex({ email: 1 }); // 单字段索引
db.products.createIndex({ category: 1, price: 1 }); // 复合索引
db.articles.createIndex({ title: "text", content: "text" }); // 文本索引

// 分析查询性能
db.users.find({ email: "user@example.com" }).explain("executionStats");

2. 查询优化原则

  • 避免全表扫描:确保查询条件能够使用索引
  • 合理使用投影:只返回必要的字段
  • 控制结果集大小:使用 limit()skip() 进行分页
  • 避免正则表达式开头通配符/^pattern//pattern/ 更高效

3. 内存管理考虑

// 大结果集处理
const cursor = db.largeCollection.find({ status: "active" });
cursor.batchSize(1000); // 控制批处理大小

// 游标超时设置
const cursor = db.collection.find(query).maxTimeMS(30000); // 30秒超时

数据一致性保障

1. 查询一致性级别

// 设置读取关注级别
db.collection.find(query).readConcern("majority");

// 设置读取偏好
db.collection.find(query).readPreference("primary");

2. 事务中的查询

// 在事务中执行查询
const session = db.getMongo().startSession();
session.startTransaction();

try {
  const user = db.users.findOne({ _id: userId }, { session });
  const orders = db.orders.find({ userId: userId }, { session });

  session.commitTransaction();
} catch (error) {
  session.abortTransaction();
  throw error;
}

安全查询实践

1. 防止注入攻击

// ❌ 危险:直接拼接用户输入
const query = { name: userInput }; // 可能被恶意利用

// ✅ 安全:使用参数化查询
const query = { name: { $regex: userInput, $options: "i" } };

// ✅ 更安全:使用白名单验证
const allowedFields = ["name", "email", "age"];
const query = {};
allowedFields.forEach((field) => {
  if (filters[field]) {
    query[field] = filters[field];
  }
});

2. 权限控制

// 基于用户角色的查询限制
function buildUserQuery(userRole, baseQuery) {
  const roleQueries = {
    admin: baseQuery,
    manager: { ...baseQuery, department: userRole.department },
    user: { ...baseQuery, createdBy: userRole.userId },
  };

  return roleQueries[userRole.level] || {};
}

监控和调试

1. 查询性能监控

// 启用慢查询日志
db.setProfilingLevel(2, { slowms: 100 });

// 分析查询性能
db.system.profile.find().sort({ ts: -1 }).limit(5);

// 使用 explain 分析执行计划
db.users.find({ email: "test@example.com" }).explain("executionStats");

2. 查询调试技巧

// 使用 explain 分析查询
const explainResult = db.users.find(query).explain("executionStats");
console.log("执行时间:", explainResult.executionStats.executionTimeMillis);
console.log("扫描文档数:", explainResult.executionStats.totalDocsExamined);
console.log("返回文档数:", explainResult.executionStats.totalDocsReturned);

// 检查索引使用情况
if (
  explainResult.executionStats.totalDocsExamined >
  explainResult.executionStats.totalDocsReturned * 10
) {
  console.warn("查询可能效率低下,建议优化索引");
}

企业级最佳实践

1. 查询设计原则

  • 单一职责:每个查询只做一件事
  • 可预测性:查询结果应该是可预测的
  • 可维护性:查询逻辑应该清晰易懂
  • 可扩展性:考虑数据量增长对查询性能的影响

2. 团队协作规范

// 查询模板和规范
const QueryTemplates = {
  // 用户查询模板
  findActiveUsers: (filters) => ({
    status: "active",
    ...filters,
    createdAt: { $gte: new Date(Date.now() - 365 * 24 * 60 * 60 * 1000) },
  }),

  // 分页查询模板
  paginatedQuery: (query, page = 1, limit = 20) => ({
    ...query,
    $skip: (page - 1) * limit,
    $limit: limit,
  }),
};

3. 生产环境注意事项

  • 查询超时设置:避免长时间运行的查询影响系统性能
  • 资源限制:合理设置查询的内存和 CPU 使用限制
  • 监控告警:设置查询性能监控和异常告警
  • 备份策略:确保查询操作不会影响数据备份

下一篇文章,我们将深入探讨 MongoDB 的高级查询技巧,包括分页查询、游标查询、查询性能优化等内容。敬请期待!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值