查询文档(Read)
查询文档(Read)
Mongoose提供了多种方法来查询文档,从简单的find
到复杂的聚合操作。查询方法返回一个Query
对象,允许链式调用进一步细化查询条件。查询结果可以是单个文档、文档数组或游标对象,具体取决于使用的查询方法。
基本查询方法
find
方法是最常用的查询方法,它返回一个包含所有匹配文档的数组。如果没有提供查询条件,则返回集合中的所有文档。
const User = mongoose.model('User', userSchema);
// 查询所有用户
User.find({}, (err, users) => {
if (err) return console.error(err);
console.log(users);
});
// 查询年龄大于18的用户
User.find({ age: { $gt: 18 } }, (err, users) => {
if (err) return console.error(err);
console.log(users);
});
findOne
方法返回第一个匹配的文档,而不是数组。这在只需要单个文档时很有用。
// 查询名字为"张三"的第一个用户
User.findOne({ name: '张三' }, (err, user) => {
if (err) return console.error(err);
console.log(user);
});
查询条件
Mongoose支持MongoDB的所有查询操作符,包括比较操作符、逻辑操作符和元素操作符等。
// 比较操作符示例
User.find({ age: { $gte: 20, $lte: 30 } }, (err, users) => {
// 查询年龄在20到30之间的用户
});
// 逻辑操作符示例
User.find({ $or: [{ age: { $lt: 18 } }, { age: { $gt: 65 } }] }, (err, users) => {
// 查询年龄小于18或大于65的用户
});
// 正则表达式查询
User.find({ name: /^张/ }, (err, users) => {
// 查询名字以"张"开头的用户
});
查询选项
查询方法可以接受选项参数,用于控制返回结果的行为。
// 限制返回字段
User.find({}, 'name age', (err, users) => {
// 只返回name和age字段
});
// 分页查询
User.find({})
.skip(10) // 跳过前10条
.limit(5) // 返回5条
.exec((err, users) => {
// 第11到15条记录
});
// 排序
User.find({})
.sort({ age: -1 }) // 按年龄降序
.exec((err, users) => {
// 排序后的结果
});
高级查询技巧
链式查询
Mongoose查询支持链式调用,可以逐步构建复杂的查询。
User.find({ age: { $gt: 18 } })
.where('name').equals('张三')
.select('name email')
.limit(5)
.sort({ age: -1 })
.exec((err, users) => {
// 复杂的查询结果
});
查询中间件
可以在查询前后添加中间件函数,执行额外的逻辑。
schema.pre('find', function(next) {
console.log('即将执行查询');
next();
});
schema.post('find', function(docs, next) {
console.log('查询完成,返回了', docs.length, '个文档');
next();
});
聚合查询
对于复杂的统计和分析需求,可以使用聚合框架。
User.aggregate([
{ $match: { age: { $gt: 18 } } },
{ $group: { _id: "$city", total: { $sum: 1 } } },
{ $sort: { total: -1 } }
]).exec((err, result) => {
// 按城市分组统计成年用户数量
});
查询性能优化
索引使用
合理使用索引可以显著提高查询性能。
// 创建索引
userSchema.index({ name: 1, age: -1 });
// 查看查询是否使用了索引
User.find({ name: '张三' }).explain((err, explanation) => {
console.log(explanation.executionStats.executionStages.inputStage.indexName);
});
批量查询优化
对于大量数据的查询,可以使用游标或批量操作。
// 使用游标处理大量数据
const cursor = User.find().cursor();
cursor.on('data', (doc) => {
// 处理每个文档
}).on('end', () => {
// 处理完成
});
// 批量查询
User.find().batchSize(100).exec((err, users) => {
// 每次从服务器获取100条记录
});
查询错误处理
正确处理查询错误是保证应用健壮性的关键。
User.find({ invalidField: 'value' })
.then(users => {
// 成功处理
})
.catch(err => {
if (err.name === 'CastError') {
console.log('字段类型错误');
} else if (err.name === 'ValidationError') {
console.log('验证错误');
} else {
console.log('其他错误', err);
}
});
查询与模式验证
查询时会自动应用模式中定义的验证规则。
const userSchema = new mongoose.Schema({
email: {
type: String,
required: true,
match: /.+\@.+\..+/
}
});
// 查询时会验证email格式
User.find({ email: 'invalid-email' }, (err) => {
// 会触发验证错误
});
虚拟字段与查询
虚拟字段不会存储在数据库中,但可以在查询结果中使用。
userSchema.virtual('fullName').get(function() {
return this.firstName + ' ' + this.lastName;
});
User.findOne().then(user => {
console.log(user.fullName); // 可以访问虚拟字段
});
查询与填充(Population)
Mongoose的填充功能可以自动替换文档中的引用字段。
const storySchema = new mongoose.Schema({
author: { type: mongoose.Schema.Types.ObjectId, ref: 'User' }
});
const Story = mongoose.model('Story', storySchema);
Story.findOne().populate('author').exec((err, story) => {
console.log(story.author.name); // 直接访问作者名字
});
地理空间查询
对于包含地理空间数据的文档,可以使用特殊的地理空间查询。
const placeSchema = new mongoose.Schema({
location: {
type: { type: String, default: 'Point' },
coordinates: { type: [Number] }
}
});
placeSchema.index({ location: '2dsphere' });
Place.find({
location: {
$near: {
$geometry: { type: "Point", coordinates: [ -73.9667, 40.78 ] },
$maxDistance: 1000
}
}
}).then(places => {
// 查询距离给定坐标1000米内的地点
});
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:创建文档(Create)
下一篇:更新文档(Update)