模型(Model)的创建与使用
模型(Model)的创建与使用
Mongoose 中的模型是 Schema 的实例,用于与 MongoDB 集合进行交互。模型提供了创建、查询、更新和删除文档的方法,是 Mongoose 的核心功能之一。
定义 Schema
在创建模型之前,需要先定义 Schema。Schema 定义了文档的结构、字段类型、默认值和验证规则等。
const mongoose = require('mongoose');
const { Schema } = mongoose;
const userSchema = new Schema({
username: {
type: String,
required: true,
unique: true,
minlength: 3,
maxlength: 20
},
email: {
type: String,
required: true,
unique: true,
match: /^\S+@\S+\.\S+$/
},
age: {
type: Number,
min: 18,
max: 120
},
createdAt: {
type: Date,
default: Date.now
},
isActive: {
type: Boolean,
default: true
}
});
创建模型
使用 mongoose.model()
方法将 Schema 编译为模型。模型名称通常使用单数形式,Mongoose 会自动将其转换为复数形式作为集合名称。
const User = mongoose.model('User', userSchema);
也可以指定自定义的集合名称:
const User = mongoose.model('User', userSchema, 'custom_users');
模型方法
模型提供了多种方法来操作数据库:
创建文档
// 创建单个文档
const newUser = new User({
username: 'john_doe',
email: 'john@example.com',
age: 30
});
newUser.save()
.then(doc => console.log(doc))
.catch(err => console.error(err));
// 或者使用 create 方法
User.create({
username: 'jane_doe',
email: 'jane@example.com',
age: 28
})
.then(doc => console.log(doc))
.catch(err => console.error(err));
查询文档
// 查找所有文档
User.find({})
.then(users => console.log(users))
.catch(err => console.error(err));
// 带条件查询
User.find({ age: { $gte: 25 } })
.then(users => console.log(users))
.catch(err => console.error(err));
// 查找单个文档
User.findOne({ username: 'john_doe' })
.then(user => console.log(user))
.catch(err => console.error(err));
// 按 ID 查找
User.findById('507f1f77bcf86cd799439011')
.then(user => console.log(user))
.catch(err => console.error(err));
更新文档
// 更新单个文档
User.updateOne(
{ username: 'john_doe' },
{ $set: { age: 31 } }
)
.then(result => console.log(result))
.catch(err => console.error(err));
// 更新多个文档
User.updateMany(
{ isActive: true },
{ $set: { lastLogin: new Date() } }
)
.then(result => console.log(result))
.catch(err => console.error(err));
// findByIdAndUpdate
User.findByIdAndUpdate(
'507f1f77bcf86cd799439011',
{ $inc: { age: 1 } },
{ new: true } // 返回更新后的文档
)
.then(user => console.log(user))
.catch(err => console.error(err));
删除文档
// 删除单个文档
User.deleteOne({ username: 'john_doe' })
.then(result => console.log(result))
.catch(err => console.error(err));
// 删除多个文档
User.deleteMany({ isActive: false })
.then(result => console.log(result))
.catch(err => console.error(err));
// findByIdAndDelete
User.findByIdAndDelete('507f1f77bcf86cd799439011')
.then(user => console.log(user))
.catch(err => console.error(err));
静态方法
可以在模型上定义静态方法:
userSchema.statics.findByUsername = function(username) {
return this.find({ username: new RegExp(username, 'i') });
};
// 使用静态方法
User.findByUsername('john')
.then(users => console.log(users))
.catch(err => console.error(err));
实例方法
也可以在文档实例上定义方法:
userSchema.methods.getProfile = function() {
return {
username: this.username,
email: this.email,
memberSince: this.createdAt.toLocaleDateString()
};
};
// 使用实例方法
User.findOne({ username: 'john_doe' })
.then(user => {
if (user) {
console.log(user.getProfile());
}
})
.catch(err => console.error(err));
查询构建器
Mongoose 提供了链式查询构建器:
User.find()
.where('age').gte(18).lte(65)
.where('isActive').equals(true)
.sort('-createdAt')
.limit(10)
.select('username email age')
.exec()
.then(users => console.log(users))
.catch(err => console.error(err));
中间件
可以在模型操作前后添加中间件:
// 保存前预处理
userSchema.pre('save', function(next) {
if (this.isModified('email')) {
this.email = this.email.toLowerCase();
}
next();
});
// 查询后处理
userSchema.post('find', function(docs) {
console.log(`Found ${docs.length} users`);
});
虚拟属性
虚拟属性不会存入数据库,但可以像普通属性一样访问:
userSchema.virtual('fullName').get(function() {
return `${this.firstName} ${this.lastName}`;
});
userSchema.virtual('fullName').set(function(name) {
const [firstName, lastName] = name.split(' ');
this.firstName = firstName;
this.lastName = lastName;
});
const user = new User();
user.fullName = 'John Doe';
console.log(user.firstName); // 'John'
console.log(user.lastName); // 'Doe'
console.log(user.fullName); // 'John Doe'
索引
可以在 Schema 中定义索引:
userSchema.index({ username: 1 }, { unique: true });
userSchema.index({ email: 1 }, { unique: true });
userSchema.index({ age: 1, isActive: 1 });
模型验证
Mongoose 提供了内置验证器:
const productSchema = new Schema({
name: {
type: String,
required: [true, '产品名称是必填项'],
trim: true
},
price: {
type: Number,
required: true,
min: [0, '价格不能小于0']
},
stock: {
type: Number,
default: 0,
validate: {
validator: Number.isInteger,
message: '库存必须是整数'
}
},
category: {
type: String,
enum: {
values: ['电子产品', '服装', '食品'],
message: '无效的商品类别'
}
}
});
关联关系
Mongoose 支持文档间的关联:
const orderSchema = new Schema({
user: {
type: Schema.Types.ObjectId,
ref: 'User',
required: true
},
products: [{
product: {
type: Schema.Types.ObjectId,
ref: 'Product'
},
quantity: Number
}],
totalAmount: Number
});
// 填充关联数据
Order.findById(orderId)
.populate('user')
.populate('products.product')
.exec()
.then(order => console.log(order))
.catch(err => console.error(err));
分页查询
实现分页查询的常见模式:
const getUsers = async (page = 1, limit = 10) => {
const skip = (page - 1) * limit;
const [users, total] = await Promise.all([
User.find().skip(skip).limit(limit),
User.countDocuments()
]);
return {
users,
total,
pages: Math.ceil(total / limit),
currentPage: page
};
};
事务处理
Mongoose 支持 MongoDB 事务:
const session = await mongoose.startSession();
session.startTransaction();
try {
const user = await User.create([{ username: 'new_user' }], { session });
await Order.create([{ user: user[0]._id, total: 100 }], { session });
await session.commitTransaction();
} catch (error) {
await session.abortTransaction();
throw error;
} finally {
session.endSession();
}
聚合管道
使用聚合框架进行复杂查询:
User.aggregate([
{ $match: { isActive: true } },
{ $group: {
_id: '$ageGroup',
count: { $sum: 1 },
averageAge: { $avg: '$age' }
}},
{ $sort: { count: -1 } }
])
.then(results => console.log(results))
.catch(err => console.error(err));
性能优化
一些模型使用中的性能优化技巧:
- 只查询需要的字段:
User.find().select('username email -_id')
- 使用 lean() 返回普通 JavaScript 对象:
User.find().lean()
- 合理使用索引:
userSchema.index({ createdAt: -1 });
- 批量操作时使用 bulkWrite:
User.bulkWrite([
{ insertOne: { document: { username: 'user1' } } },
{ updateOne: { filter: { username: 'user2' }, update: { $set: { age: 30 } } } }
])
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn