如何在NestJS中实现动态字段类型?MongoDB/Mongoose报错求助
问题分析与解决方案
核心问题
- Schema定义矛盾:当前
MenuSchema中childrens字段的@Prop配置错误——既指定type: Number又声明ref: 'Menu',但未标记数组类型;若使用自定义数字ID,也未显式配置_id类型。 - 查询未执行:
findOneAndUpdate未调用exec()触发实际数据库操作,且若用默认ObjectId,将URL参数转成数字+id会导致匹配失败。 - 未关联查询子菜单:未通过
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
相关产品推荐
相关产品推荐

