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

如何在NestJS中实现动态字段类型?MongoDB/Mongoose报错求助

问题分析与解决方案

核心问题

  1. Schema定义矛盾:当前Menu Schema中childrens字段的@Prop配置错误——既指定type: Number又声明ref: 'Menu',但未标记数组类型;若使用自定义数字ID,也未显式配置_id类型。
  2. 查询未执行:findOneAndUpdate未调用exec()触发实际数据库操作,且若用默认ObjectId,将URL参数转成数字+id会导致匹配失败。
  3. 未关联查询子菜单:未通过populate将子菜单ID数组转换为Menu[]类型返回给客户端。

具体修复步骤

1. 修正Menu Schema定义

根据ID类型分两种场景处理:

场景一:使用自定义数字ID

// menu.schema.ts
import { Schema, Prop, SchemaFactory } from '@nestjs/mongoose';
import { Document } from 'mongoose';

@Schema({ versionKey: false })
export class Menu extends Document {
  // 显式指定_id为数字类型
  @Prop({ type: Number, required: true, unique: true })
  _id: number;

  // 其他业务字段...

  // 配置childrens为数字数组,关联Menu模型
  @Prop({ type: [Number], ref: 'Menu', default: [] })
  childrens: number[]; // 存储时是ID数组,查询时通过populate转为Menu[]
}

export const MenuSchema = SchemaFactory.createForClass(Menu);

场景二:使用MongoDB默认ObjectId

若保留默认ObjectId作为ID,需同步修改DTO和控制器:

// menu.schema.ts
import { Schema, Prop, SchemaFactory } from '@nestjs/mongoose';
import { Document, SchemaTypes } from 'mongoose';

@Schema({ versionKey: false })
export class Menu extends Document {
  // 其他业务字段...

  @Prop({ type: [SchemaTypes.ObjectId], ref: 'Menu', default: [] })
  childrens: Menu[];
}

此时DTO需将childrens改为string[](ObjectId以字符串传递),控制器无需转换ID类型:

// menu.controller.ts
@Put(':id')
update(@Param('id') id: string, @Body() updateMenuDto: UpdateMenuDto) {
  return this.menuService.update(id, updateMenuDto);
}

2. 修复服务层更新逻辑

确保执行查询并返回更新后的文档,同时支持关联查询:

// menu.service.ts
import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { Menu } from './menu.schema';
import { UpdateMenuDto } from './dto/update-menu.dto';

@Injectable()
export class MenuService {
  constructor(@InjectModel(Menu.name) private readonly menuModel: Model<Menu>) {}

  async update(id: number, updateMenuDto: UpdateMenuDto) {
    // 使用findByIdAndUpdate更简洁,new: true返回更新后的文档
    const updatedMenu = await this.menuModel
      .findByIdAndUpdate(id, updateMenuDto, { new: true })
      .populate('childrens'); // 关联查询子菜单,转为Menu[]类型返回
    return updatedMenu;
  }
}

3. 保证DTO数据校验正确性

确保DTO能正确接收并校验前端传递的参数:

// update-menu.dto.ts
import { IsArray, IsNumber } from 'class-validator';
import { Type } from 'class-transformer';

export class UpdateMenuDto {
  // 其他业务字段...

  @IsArray()
  @Type(() => Number) // 自动将前端传递的字符串ID转为数字
  @IsNumber({}, { each: true })
  childrens: number[];
}

测试验证

使用REST Client测试时,确保请求ID与数据库存储的ID类型一致:

Put http://127.0.0.1:3000/api/menu/1
Content-Type: application/json

{
  "childrens": [2]
}

此时服务器会正确更新数据,并返回包含childrens(Menu[]类型)的菜单文档。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 04:35:45