删除文档(Delete)
删除文档(Delete)
在Mongoose中删除文档是常见的数据库操作之一。通过deleteOne()
、deleteMany()
和findOneAndDelete()
等方法可以灵活地移除集合中的记录。理解这些方法的差异和使用场景对高效操作MongoDB至关重要。
使用deleteOne()删除单个文档
deleteOne()
方法删除匹配条件的第一个文档。如果查询条件匹配多个文档,只有第一个被删除。该方法返回一个Promise,包含删除操作的结果信息。
const User = require('./models/user');
async function deleteUser() {
try {
const result = await User.deleteOne({ name: '张三' });
console.log(result);
// 输出类似:{ acknowledged: true, deletedCount: 1 }
} catch (error) {
console.error(error);
}
}
deleteUser();
当需要精确删除特定文档时,建议使用唯一标识符如_id
:
await User.deleteOne({ _id: '507f1f77bcf86cd799439011' });
使用deleteMany()批量删除文档
deleteMany()
删除所有匹配查询条件的文档。这在清理数据或批量操作时特别有用。
async function deleteInactiveUsers() {
try {
const result = await User.deleteMany({ lastLogin: { $lt: new Date('2023-01-01') } });
console.log(`删除了 ${result.deletedCount} 个不活跃用户`);
} catch (error) {
console.error(error);
}
}
deleteInactiveUsers();
批量删除操作需要谨慎使用,建议先执行查询确认受影响文档:
const usersToDelete = await User.find({ status: 'inactive' });
console.log(`即将删除 ${usersToDelete.length} 个用户`);
// 确认无误后再执行删除
await User.deleteMany({ status: 'inactive' });
使用findOneAndDelete()删除并返回文档
findOneAndDelete()
在删除文档的同时返回被删除的文档内容。这在需要记录删除内容或执行后续处理时很有价值。
async function deleteAndLogUser() {
try {
const deletedUser = await User.findOneAndDelete({ email: 'user@example.com' });
if (deletedUser) {
console.log('已删除用户:', deletedUser);
await sendGoodbyeEmail(deletedUser.email); // 发送告别邮件
}
} catch (error) {
console.error(error);
}
}
deleteAndLogUser();
可以通过选项参数控制返回内容:
const result = await User.findOneAndDelete(
{ username: 'test' },
{ projection: { name: 1, email: 1 } } // 只返回name和email字段
);
删除操作的钩子函数
Mongoose提供了pre和post钩子,可以在删除操作前后执行自定义逻辑。
User.pre('deleteOne', function(next) {
console.log(`准备删除用户 ${this._id}`);
// 可以在这里添加验证逻辑
next();
});
User.post('deleteOne', function(doc, next) {
console.log(`用户 ${doc._id} 已被删除`);
// 可以在这里执行清理操作
next();
});
对于批量删除,使用deleteMany
钩子:
User.pre('deleteMany', async function(next) {
const filter = this.getFilter();
const count = await User.countDocuments(filter);
console.log(`即将删除 ${count} 个用户`);
next();
});
级联删除相关数据
在关系数据中,经常需要删除主文档时同时删除关联文档。可以通过钩子实现:
User.pre('deleteOne', async function() {
const userId = this.getFilter()._id;
await Post.deleteMany({ author: userId }); // 删除用户的所有文章
await Comment.deleteMany({ user: userId }); // 删除用户的所有评论
});
或者使用MongoDB的聚合操作:
async function deleteUserWithRelatedData(userId) {
const session = await User.startSession();
session.startTransaction();
try {
await Post.deleteMany({ author: userId }).session(session);
await Comment.deleteMany({ user: userId }).session(session);
await User.deleteOne({ _id: userId }).session(session);
await session.commitTransaction();
} catch (error) {
await session.abortTransaction();
throw error;
} finally {
session.endSession();
}
}
软删除模式实现
有时需要保留数据记录而非物理删除,可以实现软删除模式:
const userSchema = new mongoose.Schema({
name: String,
email: String,
isDeleted: { type: Boolean, default: false },
deletedAt: Date
});
// 添加查询助手排除已删除文档
userSchema.query.notDeleted = function() {
return this.where({ isDeleted: false });
};
// 软删除方法
userSchema.methods.softDelete = function() {
this.isDeleted = true;
this.deletedAt = new Date();
return this.save();
};
const User = mongoose.model('User', userSchema);
// 使用示例
async function softDeleteUser(userId) {
const user = await User.findById(userId);
if (user) {
await user.softDelete();
}
}
// 查询时自动过滤已删除文档
User.notDeleted().find(); // 只返回未删除的用户
性能优化和注意事项
大量删除操作可能影响数据库性能,考虑以下优化:
- 为常用删除条件添加索引:
userSchema.index({ status: 1 }); // 为状态字段添加索引
- 分批删除大型数据集:
async function batchDelete(criteria, batchSize = 100) {
let deletedCount = 0;
while (true) {
const users = await User.find(criteria).limit(batchSize);
if (users.length === 0) break;
const ids = users.map(u => u._id);
const result = await User.deleteMany({ _id: { $in: ids } });
deletedCount += result.deletedCount;
console.log(`已删除 ${deletedCount} 个文档`);
}
return deletedCount;
}
- 监控删除操作性能:
const start = Date.now();
const result = await User.deleteMany({ age: { $lt: 18 } });
console.log(`删除操作耗时 ${Date.now() - start}ms`);
错误处理和事务管理
确保删除操作的原子性和错误恢复:
async function safeDelete(userId) {
try {
const result = await User.deleteOne({ _id: userId });
if (result.deletedCount === 0) {
throw new Error('用户不存在');
}
return { success: true };
} catch (error) {
console.error('删除失败:', error.message);
return { success: false, error: error.message };
}
}
对于关键业务操作,使用事务:
async function transactionalDelete(userId) {
const session = await mongoose.startSession();
try {
session.startTransaction();
const user = await User.findOneAndDelete({ _id: userId }, { session });
if (!user) throw new Error('用户不存在');
await Account.deleteOne({ userId }, { session });
await session.commitTransaction();
return user;
} catch (error) {
await session.abortTransaction();
throw error;
} finally {
session.endSession();
}
}
删除操作的权限控制
在应用中实现删除权限验证:
userSchema.statics.deleteWithPermission = async function(userId, requesterRole) {
if (requesterRole !== 'admin') {
throw new Error('无权执行删除操作');
}
return this.deleteOne({ _id: userId });
};
// 使用示例
async function adminDeleteUser(userId, adminId) {
const admin = await User.findById(adminId);
if (!admin || admin.role !== 'admin') {
throw new Error('权限不足');
}
return User.deleteWithPermission(userId, admin.role);
}
测试删除操作
为删除功能编写单元测试:
describe('用户删除功能', () => {
beforeEach(async () => {
await User.create({ name: '测试用户', email: 'test@example.com' });
});
it('应该成功删除用户', async () => {
const user = await User.findOne({ email: 'test@example.com' });
const result = await User.deleteOne({ _id: user._id });
expect(result.deletedCount).to.equal(1);
const deletedUser = await User.findById(user._id);
expect(deletedUser).to.be.null;
});
it('删除不存在的用户应返回0', async () => {
const result = await User.deleteOne({ _id: '000000000000000000000000' });
expect(result.deletedCount).to.equal(0);
});
});
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:更新文档(Update)
下一篇:批量操作与高效写入