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

NestJS中Mongoose一对一关联仍可创建多门店的原因问询

问题描述

我在NestJS中使用@nestjs/mongoose和mongoose实现MongoDB的一对一关联,现有owners和stores两个集合,要求每个owner只能拥有一个store。

已定义Schema如下:

owner.schema.ts

@Schema()
export class Owner {

  /* 其他字段 */

  @Prop({
    type: mongoose.Schema.Types.ObjectId,
    ref: 'Store'
  })
  store: Store;
}

store.schema.ts

@Schema()
export default class Store {

  /* 其他字段 */

  @Prop({
    type: mongoose.Schema.Types.ObjectId,
    ref: 'Owner',
    required: true,
  })
  owner: Owner;
}

store.service.ts中的创建方法:

async createStore(createStoreDto: CreateStoreDto, user: ReqUser) {
    // 创建store实体
    const entity = {
      ...createStoreDto,
      owner: user.id,
    }
    const store = await this.storeModel.create(entity);
    const saved = await store.save()

    // 将store._id保存到owner.store字段
    await this.ownerModel.updateOne({ email: user.email }, { store: saved._id })

    return saved;
  }

但目前仍能为同一个owner创建多个store,请问原因是什么?

原因分析

当前代码只实现了创建store后关联到owner的逻辑,但没有从数据库层面限制stores集合中同一个owner字段不能重复:

  • Store Schema的owner字段仅设置了required: true,没有添加唯一约束,MongoDB不会阻止同一个owner_id被多次插入到stores集合中;
  • 虽然创建store后会更新owner的store字段,但这只是覆盖旧值,无法阻止新store被创建。

解决方案

1. 给Store的owner字段添加唯一索引

修改store.schema.ts中的owner字段配置,添加unique: true,让MongoDB强制约束同一个owner只能对应一个store:

@Schema()
export default class Store {

  /* 其他字段 */

  @Prop({
    type: mongoose.Schema.Types.ObjectId,
    ref: 'Owner',
    required: true,
    unique: true // 添加唯一约束
  })
  owner: Owner;
}

注意:如果集合中已有重复的owner数据,需要先清理掉再添加该约束,否则会报错。

2. 优化创建逻辑(可选,提升用户体验)

在创建store前先检查该owner是否已拥有store,提前返回错误,避免触发数据库的唯一约束报错:

async createStore(createStoreDto: CreateStoreDto, user: ReqUser) {
    // 先检查该用户是否已有store
    const existingStore = await this.storeModel.findOne({ owner: user.id });
    if (existingStore) {
      throw new BadRequestException('该用户已拥有店铺,无法重复创建');
    }

    const entity = {
      ...createStoreDto,
      owner: user.id,
    }
    const store = await this.storeModel.create(entity);
    const saved = await store.save()

    await this.ownerModel.updateOne({ email: user.email }, { store: saved._id })

    return saved;
  }

3. 使用事务保证原子性(可选)

如果需要确保「创建store」和「更新owner」两个操作要么都成功要么都失败,可以使用MongoDB事务:

async createStore(createStoreDto: CreateStoreDto, user: ReqUser) {
    const session = await mongoose.startSession();
    session.startTransaction();
    try {
      const existingStore = await this.storeModel.findOne({ owner: user.id }).session(session);
      if (existingStore) {
        throw new BadRequestException('该用户已拥有店铺,无法重复创建');
      }

      const entity = {
        ...createStoreDto,
        owner: user.id,
      }
      const store = await this.storeModel.create([entity], { session });
      const saved = store[0];

      await this.ownerModel.updateOne(
        { email: user.email }, 
        { store: saved._id },
        { session }
      );

      await session.commitTransaction();
      return saved;
    } catch (error) {
      await session.abortTransaction();
      throw error;
    } finally {
      session.endSession();
    }
  }

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 09:05:25