阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > 删除文档(Delete)

删除文档(Delete)

作者:陈川 阅读数:39887人阅读 分类: MongoDB

删除文档(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(); // 只返回未删除的用户

性能优化和注意事项

大量删除操作可能影响数据库性能,考虑以下优化:

  1. 为常用删除条件添加索引:
userSchema.index({ status: 1 }); // 为状态字段添加索引
  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;
}
  1. 监控删除操作性能:
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

前端川

前端川,陈川的代码茶馆🍵,专治各种不服的Bug退散符💻,日常贩卖秃头警告级的开发心得🛠️,附赠一行代码笑十年的摸鱼宝典🐟,偶尔掉落咖啡杯里泡开的像素级浪漫☕。‌