You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Mongoose无更新pre hook?需实现编辑文档名称重复校验

How to Reuse Mongoose Name Uniqueness Validation for Both Save and Update Operations

Great question! I ran into this exact issue with Mongoose hooks a while back—those pre('save') hooks only fire for save() and create() calls, not for update operations like findByIdAndUpdate() or updateOne(). The good news is we can refactor your existing validation logic into a reusable function and hook it up to Mongoose's update-specific middleware.

Step 1: Extract Reusable Validation Logic

First, let's pull your name uniqueness check into a standalone async function. This fixes the this context problem in your original callback-based code and makes the logic usable across both save and update hooks:

async function validateUniqueCompanyName(doc, updateName) {
  // Use the updated name if provided, otherwise fall back to the document's current name
  const nameToCheck = updateName || doc.name;
  
  // Find companies with the same name, excluding the current document to avoid false positives
  const existingCompany = await this.model('Company').findOne({
    name: nameToCheck,
    _id: { $ne: doc._id }
  });

  if (existingCompany) {
    throw new Error("Company Name exists!");
  }
}

Step 2: Update the Pre-Save Hook

Modify your existing pre('save') hook to use this reusable function. We'll use async/await for cleaner error handling instead of nested callbacks:

company_model.companySchema.pre('save', async function(next) {
  try {
    // Pass the current document to the validation function
    await validateUniqueCompanyName.call(this, this);
    next();
  } catch (err) {
    console.error('Error in pre-save validation:', err.message);
    next(err); // Pass the error to Mongoose to trigger a failure
  }
});

Step 3: Add Hooks for Update Operations

Mongoose has separate middleware for update operations. Below are hooks for findOneAndUpdate (covers findByIdAndUpdate, which is common in API endpoints) and updateOne:

Pre-FindOneAndUpdate Hook

company_model.companySchema.pre('findOneAndUpdate', async function(next) {
  try {
    // Get the update payload from the query
    const update = this.getUpdate();
    const updatedName = update.name;

    // Skip validation if the name isn't being modified
    if (!updatedName) {
      return next();
    }

    // Fetch the current document to exclude it from the uniqueness check
    const currentDoc = await this.model('Company').findOne(this.getQuery());
    if (!currentDoc) {
      return next(new Error('Company not found'));
    }

    // Run the validation with the updated name
    await validateUniqueCompanyName.call(this, currentDoc, updatedName);
    next();
  } catch (err) {
    console.error('Error in pre-findOneAndUpdate validation:', err.message);
    next(err);
  }
});

Pre-UpdateOne Hook (for batch or direct update calls)

company_model.companySchema.pre('updateOne', async function(next) {
  try {
    const update = this.getUpdate();
    const updatedName = update.name;

    if (!updatedName) {
      return next();
    }

    const currentDoc = await this.model('Company').findOne(this.getQuery());
    if (!currentDoc) {
      return next(new Error('Company not found'));
    }

    await validateUniqueCompanyName.call(this, currentDoc, updatedName);
    next();
  } catch (err) {
    console.error('Error in pre-updateOne validation:', err.message);
    next(err);
  }
});

Key Improvements

  • Single Source of Truth: Your validation logic lives in one place, so you only need to maintain it once.
  • Fixes this Binding: No more issues with this not pointing to the document inside callbacks.
  • Handles Edge Cases: Skips validation when the name isn't changed, and excludes the current document from the duplicate check.
  • Cleaner Error Handling: async/await eliminates callback hell and makes error logging more straightforward.

Test Scenarios to Verify

Make sure to test these cases to ensure everything works as expected:

  • Creating a new company with an existing name throws the "Company Name exists!" error.
  • Editing a company to use another existing company's name throws the error.
  • Editing a company without changing its name passes validation.
  • Editing a company to use a unique name passes validation.

内容的提问来源于stack exchange,提问作者Farhan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:16:00