阿里云主机折上折
  • 微信号
Current Site:Index > Delete the document (Delete)

Delete the document (Delete)

Author:Chuan Chen 阅读数:39886人阅读 分类: MongoDB

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:

  1. Add indexes for common deletion conditions:
userSchema.index({ status: 1 }); // Add an index for the status field
  1. 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;
}
  1. 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

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.