MongoDB 数据查询方法深度解析与实践指南

一、基础查询方法详解

1.1 多记录查询(find)

find()是MongoDB最常用的查询方法,用于检索符合条件的多个文档。其核心特性包括:

  • 返回结果始终为数组类型,即使没有匹配项也会返回空数组
  • 支持链式调用投影(projection)进行字段筛选
  • 可配合分页参数实现高效数据分片
  1. // 基础查询示例
  2. const users = await UserModel.find({
  3. status: 'active',
  4. age: { $gt: 18 }
  5. }).select('name email -_id'); // 投影操作:包含name/email,排除_id
  6. // 分页实现
  7. const pageSize = 10;
  8. const usersPage = await UserModel.find({
  9. department: 'engineering'
  10. }).skip(20).limit(pageSize);

1.2 单记录查询(findOne)

当需要获取单个文档时,findOne()find().limit(1)更高效:

  • 直接返回文档对象而非数组
  • 未匹配时返回null
  • 支持所有条件运算符
  1. // 精确匹配示例
  2. const admin = await UserModel.findOne({
  3. role: 'admin',
  4. isDeleted: false
  5. });
  6. // 对象ID查询(推荐使用ObjectId类型)
  7. const { ObjectId } = require('mongodb'); // 或使用mongoose的Schema.Types.ObjectId
  8. const user = await UserModel.findOne({
  9. _id: new ObjectId('60d21b4667d0d8992e610c85')
  10. });

二、条件查询运算符体系

2.1 比较运算符

运算符 说明 示例
$eq 等于(可省略) { age: { $eq: 25 } }
$ne 不等于 { status: { $ne: 'pending' } }
$gt 大于 { score: { $gt: 90 } }
$gte 大于等于 { price: { $gte: 100 } }
$lt 小于 { temperature: { $lt: 37.5 } }
$lte 小于等于 { storage: { $lte: 512 } }

2.2 逻辑运算符

  1. // AND逻辑(默认)
  2. const result1 = await Collection.find({
  3. $and: [
  4. { age: { $gte: 18 } },
  5. { country: 'China' }
  6. ]
  7. });
  8. // OR逻辑
  9. const result2 = await Collection.find({
  10. $or: [
  11. { role: 'admin' },
  12. { permissions: { $in: ['write'] } }
  13. ]
  14. });
  15. // NOT逻辑
  16. const result3 = await Collection.find({
  17. status: { $ne: 'archived' },
  18. $nor: [
  19. { lastLogin: { $lt: new Date('2023-01-01') } },
  20. { isVerified: false }
  21. ]
  22. });

2.3 元素运算符

  1. // 字段存在性检查
  2. const activeUsers = await UserModel.find({
  3. lastLogin: { $exists: true }
  4. });
  5. // 字段类型检查
  6. const numericFields = await DataModel.find({
  7. value: { $type: 'number' } // 或使用BSON类型别名如'double'
  8. });
  9. // 空值检查
  10. const emptyRecords = await RecordModel.find({
  11. $or: [
  12. { description: { $exists: false } },
  13. { description: null },
  14. { description: '' }
  15. ]
  16. });

三、高级查询模式

3.1 数组查询

  1. // 精确匹配数组
  2. const exactMatch = await Collection.find({
  3. tags: ['mongodb', 'database'] // 必须完全匹配顺序和内容
  4. });
  5. // 包含指定元素
  6. const containsElement = await Collection.find({
  7. tags: 'mongodb' // 任意位置包含
  8. });
  9. // 元素条件查询
  10. const nestedQuery = await Collection.find({
  11. 'comments.author': 'Alice', // 嵌套字段
  12. 'comments.rating': { $gte: 4 }
  13. });
  14. // 数组长度检查
  15. const arraySizeCheck = await Collection.find({
  16. tags: { $size: 3 } // 数组必须包含3个元素
  17. });

3.2 聚合查询

countDocuments()是专门优化的计数方法,比find().count()更高效:

  1. // 基础计数
  2. const total = await OrderModel.countDocuments({
  3. status: 'completed'
  4. });
  5. // 条件计数
  6. const activeUsers = await UserModel.countDocuments({
  7. lastLogin: { $gte: new Date(Date.now() - 30*24*60*60*1000) }
  8. });
  9. // 分布式计数优化(大数据集场景)
  10. const largeCollection = db.collection('logs');
  11. const count = await largeCollection.countDocuments(
  12. { timestamp: { $gte: startDate } },
  13. { maxTimeMS: 5000 } // 设置超时
  14. );

四、查询优化实践

4.1 索引利用策略

  1. 复合索引设计
    ```javascript
    // 创建复合索引([status, createdAt])
    await UserModel.createIndexes([
    { key: { status: 1, createdAt: -1 } }
    ]);

// 查询应遵循索引顺序
const optimizedQuery = await UserModel.find({
status: ‘active’
}).sort({ createdAt: -1 });

  1. 2. **索引覆盖查询**:
  2. ```javascript
  3. // 创建只包含查询字段的索引
  4. await ProductModel.createIndex({ category: 1, price: 1 });
  5. // 完全由索引覆盖的查询(explain显示"IXSCAN"且"fetch"阶段无文档扫描)
  6. const fastQuery = await ProductModel.find({
  7. category: 'electronics'
  8. }).project({ price: 1, _id: 0 });

4.2 查询性能分析

  1. // 使用explain分析查询计划
  2. const analysis = await UserModel.find({
  3. age: { $gt: 30 }
  4. }).explain('executionStats');
  5. // 关键指标解读
  6. console.log({
  7. executionTime: analysis.executionStats.executionTimeMillis,
  8. docsExamined: analysis.executionStats.totalDocsExamined,
  9. indexUsage: analysis.executionStats.totalKeysExamined > 0
  10. });

4.3 分页优化方案

  1. // 传统skip/limit的缺陷(大数据集性能差)
  2. // 优化方案1:基于_id的范围查询
  3. async function getPageById(lastId, pageSize) {
  4. const query = lastId ? { _id: { $gt: new ObjectId(lastId) } } : {};
  5. return await UserModel.find(query)
  6. .sort({ _id: 1 })
  7. .limit(pageSize);
  8. }
  9. // 优化方案2:游标分页(需存储lastValue)
  10. async function getPageByField(lastValue, pageSize) {
  11. const query = lastValue ? {
  12. createdAt: { $lt: new Date(lastValue) }
  13. } : {};
  14. return await OrderModel.find(query)
  15. .sort({ createdAt: -1 })
  16. .limit(pageSize);
  17. }

五、常见问题解决方案

5.1 查询结果排序

  1. // 单字段排序
  2. const sortedUsers = await UserModel.find().sort({ age: -1 }); // 降序
  3. // 多字段排序
  4. const complexSort = await ProductModel.find()
  5. .sort([
  6. ['category', 1], // 升序
  7. ['price', -1] // 降序
  8. ]);
  9. // 文本搜索排序(需创建文本索引)
  10. await ArticleModel.createIndex({ content: 'text' });
  11. const searchResults = await ArticleModel.find({
  12. $text: { $search: 'mongodb performance' }
  13. }).sort({
  14. score: { $meta: 'textScore' } // 按相关性排序
  15. });

5.2 查询结果处理

  1. // 字段投影(白名单/黑名单)
  2. const minimalData = await UserModel.find()
  3. .select('name email -password -_id'); // 包含name/email,排除password/_id
  4. // 查询后处理(推荐在应用层实现)
  5. const rawData = await OrderModel.find({ status: 'pending' });
  6. const processed = rawData.map(doc => ({
  7. id: doc._id.toString(),
  8. total: doc.items.reduce((sum, item) => sum + item.price * item.quantity, 0),
  9. ...doc // 保留其他字段
  10. }));

5.3 查询安全控制

  1. // 防止NoSQL注入
  2. function safeQuery(userInput) {
  3. // 使用类型检查和默认值
  4. const safeAge = typeof userInput.age === 'number' ? userInput.age : 18;
  5. return {
  6. age: { $gte: safeAge },
  7. // 避免直接拼接用户输入到查询对象
  8. $and: userInput.roles ? [
  9. { role: { $in: Array.isArray(userInput.roles) ? userInput.roles : [userInput.roles] } }
  10. ] : []
  11. };
  12. }
  13. // 查询权限控制示例
  14. async function getUserWithPermission(userId, requiredRole) {
  15. const user = await UserModel.findOne({
  16. _id: userId,
  17. $or: [
  18. { role: requiredRole },
  19. { permissions: { $in: [requiredRole] } }
  20. ]
  21. });
  22. if (!user) throw new Error('Access denied');
  23. return user;
  24. }

本文系统梳理了MongoDB查询体系的核心方法,通过20+个实战示例展示了从基础查询到性能优化的完整技术栈。开发者应重点关注索引设计、查询计划分析和分页优化等关键环节,根据实际业务场景选择合适的查询模式。建议结合MongoDB官方文档的Query Optimization章节进行深入学习,并在生产环境实施前进行全面的性能测试。