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

编写Mongoose软删除插件:如何正确使用$concat修改唯一字段

Mongoose软删除插件:$concat操作符使用错误的解决办法

你的问题出在混淆了Mongoose实例操作和数据库更新操作的区别:this.save()是基于客户端内存中的文档实例执行保存,你直接给name字段赋值包含$concat的对象,而你的schema里name是字符串类型,Mongoose会尝试把这个对象转成字符串,自然抛出类型转换错误。

要使用$concat这类MongoDB数据库层面的操作符,你需要用数据库更新方法(比如updateOne)而非实例的save()方法,因为只有数据库更新操作才支持解析操作符。

解决方案1:使用原子更新(推荐)

直接调用文档实例的updateOne方法,在数据库层面完成软删除标记和字段重命名,这是原子操作,避免并发修改导致的问题:

schema.methods.delete = async function () {
  return this.updateOne({
    $set: {
      deleted: true,
      name: { $concat: ["$name", "_deleted"] }
    }
  });
};

注意:$concat要求MongoDB版本3.2及以上,如果你的版本过低,需要升级或者用下面的客户端拼接方案。

解决方案2:客户端拼接后保存

如果不想用数据库操作符,可以先在客户端获取当前name值,拼接后缀后再保存:

schema.methods.delete = async function () {
  this.deleted = true;
  this.name = `${this.name}_deleted`;
  return this.save();
};

这种方式的缺点是:如果在你获取文档到执行save()的间隙,有其他请求修改了name字段,会覆盖掉对方的修改,安全性不如原子更新。

扩展优化:动态处理所有唯一字段

如果你的schema里有多个唯一字段,不想硬编码name,可以遍历schema的paths来自动识别唯一字段:

schema.methods.delete = async function () {
  const update = { $set: { deleted: true } };
  
  // 遍历schema的所有字段,找出带unique约束的字符串字段
  Object.keys(schema.paths).forEach(path => {
    const field = schema.paths[path];
    if (field.options.unique && field.instance === 'String') {
      update.$set[path] = { $concat: [`$${path}`, "_deleted"] };
    }
  });
  
  return this.updateOne(update);
};

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 01:20:37