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

MongoDB不同文档数组重复ID生成问题及解决方案咨询

解决MongoDB批量更新数组元素时重复生成ObjectId的问题

问题场景

使用updateMany给city集合所有文档的tags数组添加元素时,发现所有新插入的tag子文档ID完全重复(例如63ec8f47efbd82c8face341a)。

执行代码

await this.cityRepository.updateMany(
    {},
    {
      $push: {
        tags: {
          title: tagValue.title,
          description: tagValue.description,
          applyToAllCity: tagValue.cityId ? false : true,
        },
      },
    },
);

City Schema定义

export class BaseCity extends Document {
  @Prop({
    type: "String",
    required: true,
  })
  _id: string;

  @Prop({ type: "String", unique: true })
  code: string;
  @Prop({ type: "String", ref: Province.name })
  province: string | Province;
  @Prop({ type: "String" })
  faName: string;
}

@Schema({ timestamps: true })
@Schema({ collection: "city", virtuals: true, _id: false, timestamps: true })
export class City extends BaseCity {
  @Prop({ type: "String" })
  imageName: string;

  @Prop({ index: true, type: "String" })
  enName: string;

  @Prop({ type: "Number" })
  displayOrder: number;

  @Prop({ type: "Boolean" })
  isFeatured: boolean;

  @Prop({ type: "Boolean" })
  isEnabled: boolean;

  @Prop({ type: Coordinate })
  coordinate: Coordinate;

  @Prop([{ type: Region, ref: Region.name, default: [] }])
  region: Region[];

  @Prop([{ type: SubMenu }])
  subMenu: SubMenu[];
  @Prop([{ type: CityTags }])
  tags: CityTags[];
}

const CitySchema = SchemaFactory.createForClass(City);

CitySchema.index({ faName: "text" });

export { CitySchema };

问题原因

Mongoose在执行updateMany这类批量更新操作时,只会生成一次子文档的ObjectId,并将这个ID复用给所有被更新文档的数组元素。因为updateMany是单次数据库请求,Mongoose不会为每个目标文档单独生成新的唯一ID。

解决方案

方案1:手动生成唯一ID

在构造tags元素时,手动为_id字段赋值新的ObjectId,确保每个子文档ID唯一:

import { ObjectId } from 'mongoose';

await this.cityRepository.updateMany(
  {},
  {
    $push: {
      tags: {
        _id: new ObjectId(), // 手动生成唯一ID
        title: tagValue.title,
        description: tagValue.description,
        applyToAllCity: tagValue.cityId ? false : true,
      },
    },
  },
);

方案2:逐个文档单独更新

如果希望依赖Mongoose自动生成ID,可以先查询所有city文档,再逐个调用updateOne更新。这种方式会为每个子文档生成独立ID,但文档数量较多时会增加数据库请求次数,影响性能:

const cities = await this.cityRepository.find({});

for (const city of cities) {
  await this.cityRepository.updateOne(
    { _id: city._id },
    {
      $push: {
        tags: {
          title: tagValue.title,
          description: tagValue.description,
          applyToAllCity: tagValue.cityId ? false : true,
        },
      },
    },
  );
}

方案3:禁用子文档自动生成ID

如果业务上不需要CityTags子文档的_id字段,可以在CityTags的Schema定义中设置_id: false,彻底避免ID生成和重复问题:

@Schema({ _id: false }) // 添加此配置
export class CityTags {
  // 你的字段定义,例如:
  @Prop({ type: String })
  title: string;

  @Prop({ type: String })
  description: string;

  @Prop({ type: Boolean })
  applyToAllCity: boolean;
}

内容的提问来源于stack exchange,提问作者Amir Mohammad Beheshti

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 20:46:34