Delete the document (Delete)
Deleting Documents (Delete)
In Mongoose, deleting documents is one of the common database operations. Methods like deleteOne()
, deleteMany()
, and findOneAndDelete()
allow flexible removal of records from collections. Understanding the differences and use cases of these methods is crucial for efficiently operating MongoDB.
Using deleteOne()
to Delete a Single Document
The deleteOne()
method deletes the first document that matches the condition. If the query matches multiple documents, only the first one is deleted. This method returns a Promise containing the result information of the deletion operation.
const User = require('./models/user');
async function deleteUser() {
try {
const result = await User.deleteOne({ name: '张三' });
console.log(result);
// Output similar to: { acknowledged: true, deletedCount: 1 }
} catch (error) {
console.error(error);
}
}
deleteUser();
When you need to precisely delete a specific document, it's recommended to use a unique identifier like _id
:
await User.deleteOne({ _id: '507f1f77bcf86cd799439011' });
Using deleteMany()
to Batch Delete Documents
deleteMany()
deletes all documents that match the query condition. This is particularly useful for data cleanup or batch operations.
async function deleteInactiveUsers() {
try {
const result = await User.deleteMany({ lastLogin: { $lt: new Date('2023-01-01') } });
console.log(`Deleted ${result.deletedCount} inactive users`);
} catch (error) {
console.error(error);
}
}
deleteInactiveUsers();
Batch deletion operations should be used with caution. It's recommended to first execute a query to confirm the affected documents:
const usersToDelete = await User.find({ status: 'inactive' });
console.log(`About to delete ${usersToDelete.length} users`);
// Confirm before executing the deletion
await User.deleteMany({ status: 'inactive' });
Using findOneAndDelete()
to Delete and Return a Document
findOneAndDelete()
deletes a document while returning its content. This is valuable when you need to record the deleted content or perform subsequent processing.
async function deleteAndLogUser() {
try {
const deletedUser = await User.findOneAndDelete({ email: 'user@example.com' });
if (deletedUser) {
console.log('Deleted user:', deletedUser);
await sendGoodbyeEmail(deletedUser.email); // Send a farewell email
}
} catch (error) {
console.error(error);
}
}
deleteAndLogUser();
You can control the returned content using option parameters:
const result = await User.findOneAndDelete(
{ username: 'test' },
{ projection: { name: 1, email: 1 } } // Only return the name and email fields
);
Hooks for Deletion Operations
Mongoose provides pre and post hooks to execute custom logic before and after deletion operations.
User.pre('deleteOne', function(next) {
console.log(`Preparing to delete user ${this._id}`);
// Add validation logic here
next();
});
User.post('deleteOne', function(doc, next) {
console.log(`User ${doc._id} has been deleted`);
// Perform cleanup operations here
next();
});
For batch deletions, use the deleteMany
hook:
User.pre('deleteMany', async function(next) {
const filter = this.getFilter();
const count = await User.countDocuments(filter);
console.log(`About to delete ${count} users`);
next();
});
Cascading Deletion of Related Data
In relational data, it's often necessary to delete related documents when deleting a primary document. This can be achieved using hooks:
User.pre('deleteOne', async function() {
const userId = this.getFilter()._id;
await Post.deleteMany({ author: userId }); // Delete all posts by the user
await Comment.deleteMany({ user: userId }); // Delete all comments by the user
});
Alternatively, use MongoDB's aggregation operations:
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();
}
}
Implementing Soft Delete Pattern
Sometimes, you may need to retain data records instead of physically deleting them. This can be achieved with a soft delete pattern:
const userSchema = new mongoose.Schema({
name: String,
email: String,
isDeleted: { type: Boolean, default: false },
deletedAt: Date
});
// Add a query helper to exclude deleted documents
userSchema.query.notDeleted = function() {
return this.where({ isDeleted: false });
};
// Soft delete method
userSchema.methods.softDelete = function() {
this.isDeleted = true;
this.deletedAt = new Date();
return this.save();
};
const User = mongoose.model('User', userSchema);
// Usage example
async function softDeleteUser(userId) {
const user = await User.findById(userId);
if (user) {
await user.softDelete();
}
}
// Automatically filter out deleted documents when querying
User.notDeleted().find(); // Only returns non-deleted users
Performance Optimization and Considerations
Large-scale deletion operations can impact database performance. Consider the following optimizations:
- Add indexes for common deletion conditions:
userSchema.index({ status: 1 }); // Add an index for the status field
- Delete large datasets in batches:
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(`Deleted ${deletedCount} documents`);
}
return deletedCount;
}
- Monitor deletion operation performance:
const start = Date.now();
const result = await User.deleteMany({ age: { $lt: 18 } });
console.log(`Deletion operation took ${Date.now() - start}ms`);
Error Handling and Transaction Management
Ensure atomicity and error recovery for deletion operations:
async function safeDelete(userId) {
try {
const result = await User.deleteOne({ _id: userId });
if (result.deletedCount === 0) {
throw new Error('User does not exist');
}
return { success: true };
} catch (error) {
console.error('Deletion failed:', error.message);
return { success: false, error: error.message };
}
}
For critical business operations, use transactions:
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('User does not exist');
await Account.deleteOne({ userId }, { session });
await session.commitTransaction();
return user;
} catch (error) {
await session.abortTransaction();
throw error;
} finally {
session.endSession();
}
}
Permission Control for Deletion Operations
Implement permission validation for deletion operations in your application:
userSchema.statics.deleteWithPermission = async function(userId, requesterRole) {
if (requesterRole !== 'admin') {
throw new Error('Unauthorized to perform deletion');
}
return this.deleteOne({ _id: userId });
};
// Usage example
async function adminDeleteUser(userId, adminId) {
const admin = await User.findById(adminId);
if (!admin || admin.role !== 'admin') {
throw new Error('Insufficient permissions');
}
return User.deleteWithPermission(userId, admin.role);
}
Testing Deletion Operations
Write unit tests for deletion functionality:
describe('User Deletion Functionality', () => {
beforeEach(async () => {
await User.create({ name: 'Test User', email: 'test@example.com' });
});
it('should successfully delete a user', 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('should return 0 when deleting a non-existent user', async () => {
const result = await User.deleteOne({ _id: '000000000000000000000000' });
expect(result.deletedCount).to.equal(0);
});
});
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:更新文档(Update)
下一篇:批量操作与高效写入