Typegoose父对象Ref返回null,子对象已关联父对象
问题描述
我尝试结合TypeGraphQL实现Typegoose文档中的虚拟填充示例,代码实现如下。在Apollo Studio中,先创建Person对象,再创建关联该Person的Kitten对象后,查询Kitten能看到正确的parent信息,但查询Person的pet字段始终为null。期望Person的pet字段能显示关联的Kitten,请问是代码存在错误,还是对Typegoose Ref的功能理解有误?
子对象Kitten类型定义
export enum Behavior { A = "good kitten", B = "very good kitten", C = "naughty kitten", } @ObjectType() export class Kitten { @Field((type) => ID) readonly _id: string; @Field((type) => String) @prop({ required: false }) parent: Ref<Person>; @Field((type) => String) @prop({ type: () => String, required: true }) name: string; @Field((type) => String) @prop({ type: () => String, required: true, enum: Behavior, default: Behavior.B, }) behavior?: Behavior; }
父对象Person类型定义
@ObjectType() export class Person { @Field((type) => ID) readonly _id: string; @Field((type) => String) @prop({ type: () => String, required: true }) name: string; @Field((type) => [Kitten], { nullable: true }) @prop({ ref: () => Kitten, localField: () => "_id", foreignField: () => "parent", }) pet?: Ref<Kitten>[]; }
输入类型定义
@InputType() export class NewKitten { @Field((type) => String) parent: Ref<Person>; @Field((type) => String) name: string; @Field((type) => String, { defaultValue: Behavior.B }) behavior: Behavior; } @InputType() export class NewPerson { @Field((type) => String) name: string; @Field((type) => [NewKitten], { nullable: true }) pet?: Ref<NewKitten>[]; }
模型处理文件
import { Person } from "../types/person.type"; import { Kitten } from "../types/kitten.type"; import { getModelForClass } from "@typegoose/typegoose"; export const PersonModel = getModelForClass(Person); export const KittenModel = getModelForClass(Kitten);
Resolver定义
@Resolver() export class KittenResolver { @Query((returns) => [Kitten]) async kittens(): Promise<Kitten[]> { return await KittenModel.find(); } @Mutation((returns) => Kitten) async addKitten(@Arg("addKitten") addKitten: NewKitten): Promise<Kitten> { return await KittenModel.create(addKitten); } } @Resolver() export class PersonResolver { @Query((returns) => [Person]) async people(): Promise<Person[]> { return await PersonModel.find(); } @Mutation((returns) => Person) async addPerson(@Arg("addPerson") addPerson: NewPerson): Promise<Person> { return await PersonModel.create(addPerson); } }
package.json依赖
{ "name": "ref-typegoose-example", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "dev": "ts-node-dev --respawn --transpile-only src/index.ts" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { "@typegoose/typegoose": "^9.11.0", "apollo-server": "^3.10.0", "class-validator": "^0.13.2", "config": "^3.3.7", "cors": "^2.8.5", "graphql": "^15.3.0", "mongoose": "^6.5.2", "reflect-metadata": "^0.1.13", "type-graphql": "^1.1.1" }, "devDependencies": { "@types/config": "^3.3.0", "ts-node-dev": "^2.0.0", "typescript": "^4.7.4" } }
问题分析与解决方案
你的代码存在两处关键错误,导致虚拟填充未生效:
1. 虚拟填充的装饰器使用错误
Typegoose中实现虚拟填充需要使用@virtualPopulate()装饰器,而非普通的@prop()。你在Person类的pet字段上用了@prop()并传入ref/localField/foreignField,这是错误的用法——这些参数是@virtualPopulate()的配置项,不是@prop()的。
修改Person类的pet字段定义:
@ObjectType() export class Person { // ... 其他字段 @Field((type) => [Kitten], { nullable: true }) @virtualPopulate({ ref: () => Kitten, localField: "_id", foreignField: "parent", }) pet?: Kitten[]; // 这里不需要Ref,因为虚拟填充返回的是实际文档 }
2. 查询时未触发虚拟填充
虚拟填充不会自动执行,必须在查询时调用.populate()方法指定要填充的虚拟字段。修改PersonResolver中的查询方法:
@Resolver() export class PersonResolver { @Query((returns) => [Person]) async people(): Promise<Person[]> { // 调用populate触发虚拟填充 return await PersonModel.find().populate("pet"); } // ... 其他方法 }
额外的输入类型问题
NewPerson中的pet字段定义不合理:因为创建Person时,Kitten还未被创建(或者不需要在这里关联),虚拟填充是基于Kitten中的parent字段反向关联的,所以NewPerson不需要pet字段,直接移除即可:
@InputType() export class NewPerson { @Field((type) => String) name: string; // 移除pet字段 }
这样修改后,查询Person时就能正确返回关联的Kitten列表了。
内容的提问来源于stack exchange,提问作者M G
相关产品推荐
相关产品推荐

