一、基础查询方法详解
1.1 多记录查询(find)
find()是MongoDB最常用的查询方法,用于检索符合条件的多个文档。其核心特性包括:
- 返回结果始终为数组类型,即使没有匹配项也会返回空数组
- 支持链式调用投影(projection)进行字段筛选
- 可配合分页参数实现高效数据分片
// 基础查询示例const users = await UserModel.find({status: 'active',age: { $gt: 18 }}).select('name email -_id'); // 投影操作:包含name/email,排除_id// 分页实现const pageSize = 10;const usersPage = await UserModel.find({department: 'engineering'}).skip(20).limit(pageSize);
1.2 单记录查询(findOne)
当需要获取单个文档时,findOne()比find().limit(1)更高效:
- 直接返回文档对象而非数组
- 未匹配时返回null
- 支持所有条件运算符
// 精确匹配示例const admin = await UserModel.findOne({role: 'admin',isDeleted: false});// 对象ID查询(推荐使用ObjectId类型)const { ObjectId } = require('mongodb'); // 或使用mongoose的Schema.Types.ObjectIdconst user = await UserModel.findOne({_id: new ObjectId('60d21b4667d0d8992e610c85')});
二、条件查询运算符体系
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 逻辑运算符
// AND逻辑(默认)const result1 = await Collection.find({$and: [{ age: { $gte: 18 } },{ country: 'China' }]});// OR逻辑const result2 = await Collection.find({$or: [{ role: 'admin' },{ permissions: { $in: ['write'] } }]});// NOT逻辑const result3 = await Collection.find({status: { $ne: 'archived' },$nor: [{ lastLogin: { $lt: new Date('2023-01-01') } },{ isVerified: false }]});
2.3 元素运算符
// 字段存在性检查const activeUsers = await UserModel.find({lastLogin: { $exists: true }});// 字段类型检查const numericFields = await DataModel.find({value: { $type: 'number' } // 或使用BSON类型别名如'double'});// 空值检查const emptyRecords = await RecordModel.find({$or: [{ description: { $exists: false } },{ description: null },{ description: '' }]});
三、高级查询模式
3.1 数组查询
// 精确匹配数组const exactMatch = await Collection.find({tags: ['mongodb', 'database'] // 必须完全匹配顺序和内容});// 包含指定元素const containsElement = await Collection.find({tags: 'mongodb' // 任意位置包含});// 元素条件查询const nestedQuery = await Collection.find({'comments.author': 'Alice', // 嵌套字段'comments.rating': { $gte: 4 }});// 数组长度检查const arraySizeCheck = await Collection.find({tags: { $size: 3 } // 数组必须包含3个元素});
3.2 聚合查询
countDocuments()是专门优化的计数方法,比find().count()更高效:
// 基础计数const total = await OrderModel.countDocuments({status: 'completed'});// 条件计数const activeUsers = await UserModel.countDocuments({lastLogin: { $gte: new Date(Date.now() - 30*24*60*60*1000) }});// 分布式计数优化(大数据集场景)const largeCollection = db.collection('logs');const count = await largeCollection.countDocuments({ timestamp: { $gte: startDate } },{ maxTimeMS: 5000 } // 设置超时);
四、查询优化实践
4.1 索引利用策略
- 复合索引设计:
```javascript
// 创建复合索引([status, createdAt])
await UserModel.createIndexes([
{ key: { status: 1, createdAt: -1 } }
]);
// 查询应遵循索引顺序
const optimizedQuery = await UserModel.find({
status: ‘active’
}).sort({ createdAt: -1 });
2. **索引覆盖查询**:```javascript// 创建只包含查询字段的索引await ProductModel.createIndex({ category: 1, price: 1 });// 完全由索引覆盖的查询(explain显示"IXSCAN"且"fetch"阶段无文档扫描)const fastQuery = await ProductModel.find({category: 'electronics'}).project({ price: 1, _id: 0 });
4.2 查询性能分析
// 使用explain分析查询计划const analysis = await UserModel.find({age: { $gt: 30 }}).explain('executionStats');// 关键指标解读console.log({executionTime: analysis.executionStats.executionTimeMillis,docsExamined: analysis.executionStats.totalDocsExamined,indexUsage: analysis.executionStats.totalKeysExamined > 0});
4.3 分页优化方案
// 传统skip/limit的缺陷(大数据集性能差)// 优化方案1:基于_id的范围查询async function getPageById(lastId, pageSize) {const query = lastId ? { _id: { $gt: new ObjectId(lastId) } } : {};return await UserModel.find(query).sort({ _id: 1 }).limit(pageSize);}// 优化方案2:游标分页(需存储lastValue)async function getPageByField(lastValue, pageSize) {const query = lastValue ? {createdAt: { $lt: new Date(lastValue) }} : {};return await OrderModel.find(query).sort({ createdAt: -1 }).limit(pageSize);}
五、常见问题解决方案
5.1 查询结果排序
// 单字段排序const sortedUsers = await UserModel.find().sort({ age: -1 }); // 降序// 多字段排序const complexSort = await ProductModel.find().sort([['category', 1], // 升序['price', -1] // 降序]);// 文本搜索排序(需创建文本索引)await ArticleModel.createIndex({ content: 'text' });const searchResults = await ArticleModel.find({$text: { $search: 'mongodb performance' }}).sort({score: { $meta: 'textScore' } // 按相关性排序});
5.2 查询结果处理
// 字段投影(白名单/黑名单)const minimalData = await UserModel.find().select('name email -password -_id'); // 包含name/email,排除password/_id// 查询后处理(推荐在应用层实现)const rawData = await OrderModel.find({ status: 'pending' });const processed = rawData.map(doc => ({id: doc._id.toString(),total: doc.items.reduce((sum, item) => sum + item.price * item.quantity, 0),...doc // 保留其他字段}));
5.3 查询安全控制
// 防止NoSQL注入function safeQuery(userInput) {// 使用类型检查和默认值const safeAge = typeof userInput.age === 'number' ? userInput.age : 18;return {age: { $gte: safeAge },// 避免直接拼接用户输入到查询对象$and: userInput.roles ? [{ role: { $in: Array.isArray(userInput.roles) ? userInput.roles : [userInput.roles] } }] : []};}// 查询权限控制示例async function getUserWithPermission(userId, requiredRole) {const user = await UserModel.findOne({_id: userId,$or: [{ role: requiredRole },{ permissions: { $in: [requiredRole] } }]});if (!user) throw new Error('Access denied');return user;}
本文系统梳理了MongoDB查询体系的核心方法,通过20+个实战示例展示了从基础查询到性能优化的完整技术栈。开发者应重点关注索引设计、查询计划分析和分页优化等关键环节,根据实际业务场景选择合适的查询模式。建议结合MongoDB官方文档的Query Optimization章节进行深入学习,并在生产环境实施前进行全面的性能测试。