LBe*_*rus 23 schema reference subdocument nestjs
我正在将我的应用程序从 express.js 移动到 Nest.js,并且我找不到一种方法来引用另一个 mongoose Schema,而不使用使用 mongoose.Schema({...}) 声明 Schema 的旧方法。
让我们使用文档中的示例,以便我可以澄清我的问题:
@Schema()
export class Cat extends Document {
@Prop()
name: string;
}
export const CatSchema = SchemaFactory.createForClass(Cat);
Run Code Online (Sandbox Code Playgroud)
现在,我想要的是这样的:
@Schema()
export class Owner extends Document {
@Prop({type: [Cat], required: true})
cats: Cat[];
}
export const OwnerSchema = SchemaFactory.createForClass(Owner);
Run Code Online (Sandbox Code Playgroud)
当我以这种方式定义模式时,我会收到一个错误,类似于:无效的模式配置:Cat
不是数组中的有效类型cats
那么,使用这种更面向对象的方法来定义架构,在另一个架构中引用一个架构的正确方法是什么?
Edw*_*ony 45
我深入研究了源代码,了解了该SchemaFactory.createForClass
方法如何转换 Schema 类。
@Schema()
export class Cat extends Document {
@Prop()
name: string;
}
export const catSchema = SchemaFactory.createForClass(Cat);
Run Code Online (Sandbox Code Playgroud)
基本上,当你做 SchemaFactory.createForClass(Cat)
Nest 会将 class 语法转换为 Mongoose schema 语法,所以最终转换的结果是这样的:
const schema = new mongoose.Schema({
name: { type: String } // Notice that `String` is now uppercase.
});
Run Code Online (Sandbox Code Playgroud)
看看这个文件:mongoose/prop.decorator.ts at master · nestjs/mongoose · GitHub
export function Prop(options?: PropOptions): PropertyDecorator {
return (target: object, propertyKey: string | symbol) => {
options = (options || {}) as mongoose.SchemaTypeOpts<unknown>;
const isRawDefinition = options[RAW_OBJECT_DEFINITION];
if (!options.type && !Array.isArray(options) && !isRawDefinition) {
const type = Reflect.getMetadata(TYPE_METADATA_KEY, target, propertyKey);
if (type === Array) {
options.type = [];
} else if (type && type !== Object) {
options.type = type;
}
}
TypeMetadataStorage.addPropertyMetadata({
target: target.constructor,
propertyKey: propertyKey as string,
options,
});
};
}
Run Code Online (Sandbox Code Playgroud)
在这里你可以看到Prop()
装饰器在幕后做了什么。当你这样做时:
@Prop()
name: string;
Run Code Online (Sandbox Code Playgroud)
Prop
函数将被调用,在这种情况下没有参数。
const type = Reflect.getMetadata(TYPE_METADATA_KEY, target, propertyKey);
Run Code Online (Sandbox Code Playgroud)
使用Reflect
API,我们可以获得您在执行时使用的数据类型name: string
。type
变量的值现在设置为String
。请注意,它不是string
,Reflect
API 将始终返回数据类型的构造函数版本,因此:
number
将被序列化为 Number
string
将被序列化为 String
boolean
将被序列化为 Boolean
TypeMetadataStorage.addPropertyMetadata
然后将下面的对象存储到商店中。
{
target: User,
propertyKey: ‘name’,
options: { type: String }
}
Run Code Online (Sandbox Code Playgroud)
我们来看看:mongoose/type-metadata.storage.ts at master · nestjs/mongoose · GitHub
export class TypeMetadataStorageHost {
private schemas = new Array<SchemaMetadata>();
private properties = new Array<PropertyMetadata>();
addPropertyMetadata(metadata: PropertyMetadata) {
this.properties.push(metadata);
}
}
Run Code Online (Sandbox Code Playgroud)
所以基本上这个对象将被存储到properties
变量TypeMetadataStorageHost
。
TypeMetadataStorageHost
是一个将存储大量这些对象的单例。
要了解如何SchemaFactory.createForClass(Cat)
生成 Mongoose 模式,请查看:mongoose/schema.factory.ts at master · nestjs/mongoose · GitHub
export class SchemaFactory {
static createForClass(target: Type<unknown>) {
const schemaDefinition = DefinitionsFactory.createForClass(target);
const schemaMetadata = TypeMetadataStorage.getSchemaMetadataByTarget(
target,
);
return new mongoose.Schema(
schemaDefinition,
schemaMetadata && schemaMetadata.options,
);
}
}
Run Code Online (Sandbox Code Playgroud)
最重要的部分是:
const schemaDefinition = DefinitionsFactory.createForClass(target);
。请注意,这里的目标是您的Cat
班级。
你可以在这里看到方法定义:mongoose/definitions.factory.ts at master · nestjs/mongoose · GitHub
export class DefinitionsFactory {
static createForClass(target: Type<unknown>): mongoose.SchemaDefinition {
let schemaDefinition: mongoose.SchemaDefinition = {};
schemaMetadata.properties?.forEach((item) => {
const options = this.inspectTypeDefinition(item.options as any);
schemaDefinition = {
[item.propertyKey]: options as any,
…schemaDefinition,
};
});
return schemaDefinition;
}
Run Code Online (Sandbox Code Playgroud)
schemaMetadata.properties
包含您在执行时存储的对象TypeMetadataStorage.addPropertyMetadata
:
[
{
target: User,
propertyKey: ‘name’,
options: { type: String }
}
]
Run Code Online (Sandbox Code Playgroud)
该forEach
会产生:
{
name: { type: String }
}
Run Code Online (Sandbox Code Playgroud)
最后,它将被用作master · nestjs/mongoose · GitHub 上的mongoose.Schema
构造函数mongoose/schema.factory.ts的参数:
return new mongoose.Schema(
schemaDefinition,
schemaMetadata && schemaMetadata.options,
);
Run Code Online (Sandbox Code Playgroud)
你应该把什么作为 Prop()
论点?
还记得 Nest 是在什么时候forEach
生成 Mongoose Schema 的吗?
schemaMetadata.properties?.forEach((item) => {
const options = this.inspectTypeDefinition(item.options as any);
schemaDefinition = {
[item.propertyKey]: options as any,
…schemaDefinition,
};
});
Run Code Online (Sandbox Code Playgroud)
要获得options
它使用的inspectTypeDefinition
方法。你可以看到下面的定义:
private static inspectTypeDefinition(options: mongoose.SchemaTypeOpts<unknown> | Function): PropOptions {
if (typeof options === 'function') {
if (this.isPrimitive(options)) {
return options;
} else if (this.isMongooseSchemaType(options)) {
return options;
}
return this.createForClass(options as Type<unknown>);
} else if (typeof options.type === 'function') {
options.type = this.inspectTypeDefinition(options.type);
return options;
} else if (Array.isArray(options)) {
return options.length > 0
? [this.inspectTypeDefinition(options[0])]
: options;
}
return options;
}
Run Code Online (Sandbox Code Playgroud)
options
是function
诸如String
或一个SchemaType
将被直接返回,并用作猫鼬选项。options
是Array
,它将返回该数组的第一个索引并将其包装在一个数组中。options
不是 anArray
或function
,例如,如果它只是一个普通的,object
例如{ type: String, required: true }
,它将直接返回并用作 Mongoose 选项。因此,要增加从引用Cat
到Owner
,你可以这样做:
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { Document, Schema as MongooseSchema } from 'mongoose';
import { Owner } from './owner.schema.ts';
@Schema()
export class Cat extends Document {
@Prop()
name: string;
@Prop({ type: MongooseSchema.Types.ObjectId, ref: Owner.name })
owner: Owner;
}
export const catSchema = SchemaFactory.createForClass(Cat);
Run Code Online (Sandbox Code Playgroud)
至于如何添加从基准Owner
到Cat
,我们可以这样做:
@Prop([{ type: MongooseSchema.Types.ObjectId, ref: Cat.name }])
Run Code Online (Sandbox Code Playgroud)
回答评论部分中的问题:
如果你正确阅读了答案,你应该有足够的知识来做到这一点。但如果你没有,这里是 TLDR 答案。
请注意,我强烈建议您在去这里之前阅读整个答案。
image-variant.schema.ts
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
@Schema()
export class ImageVariant {
@Prop()
url: string;
@Prop()
width: number;
@Prop()
height: number;
@Prop()
size: number;
}
export const imageVariantSchema = SchemaFactory.createForClass(ImageVariant);
Run Code Online (Sandbox Code Playgroud)
image.schema.ts
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { Document } from 'mongoose';
import { imageVariantSchema, ImageVariant } from './imagevariant.schema';
@Schema()
export class Image extends Document {
@Prop({ type: imageVariantSchema })
large: ImageVariant;
@Prop({ type: imageVariantSchema })
medium: ImageVariant;
@Prop({ type: imageVariantSchema })
small: ImageVariant;
}
export const imageSchema = SchemaFactory.createForClass(Image);
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
7718 次 |
最近记录: |