gpt4 book ai didi

reference - Nest.js 中的 Mongoose 子文档

转载 作者:行者123 更新时间:2023-12-04 11:08:24 25 4
gpt4 key购买 nike

我正在将我的应用程序从 express.js 移动到 Nest.js,并且我无法找到一种方法来引用另一个 mongoose Schema,而不使用使用 mongoose.Schema({...}) 声明 Schema 的旧方法。
让我们使用文档中的示例,以便我可以澄清我的问题:

@Schema()
export class Cat extends Document {
@Prop()
name: string;
}

export const CatSchema = SchemaFactory.createForClass(Cat);
现在,我想要的是这样的:
@Schema()
export class Owner extends Document {
@Prop({type: [Cat], required: true})
cats: Cat[];
}

export const OwnerSchema = SchemaFactory.createForClass(Owner);
当我以这种方式定义模式时,我会得到一个错误,如下所示: 无效的架构配置:Cat不是有效的
在数组中输入 cats

那么,使用这种更面向对象的方法来定义架构,在另一个架构中引用一个架构的正确方法是什么?

最佳答案

我深入研究了源代码,了解了 SchemaFactory.createForClass 是如何转换 Schema 类的。方法。
那么它是如何工作的呢?
1. 看看下面这个例子:

@Schema()
export class Cat extends Document {
@Prop()
name: string;
}
export const catSchema = SchemaFactory.createForClass(Cat);
基本上,当你做 SchemaFactory.createForClass(Cat)Nest 会将 class 语法转换为 Mongoose schema 语法,所以最终转换的结果是这样的:
const schema = new mongoose.Schema({
name: { type: String } // Notice that `String` is now uppercase.
});
2. 转换是如何工作的?
看看这个文件: 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,
});
};
}
在这里你可以看到 Prop()装饰者在幕后做。
当你这样做时:
@Prop()
name: string;
Prop函数将被调用,在这种情况下没有参数。
const type = Reflect.getMetadata(TYPE_METADATA_KEY, target, propertyKey);
使用 Reflect API,我们可以获取你做的时候使用的数据类型 name: string . type的值变量现在设置为 String .请注意,它不是 string , Reflect API 将始终返回数据类型的构造函数版本,因此:
  • number将被序列化为 Number
  • string将被序列化为 String
  • boolean将被序列化为 Boolean
  • 等等
  • TypeMetadataStorage.addPropertyMetadata然后将下面的对象存储到商店中。
    {
    target: User,
    propertyKey: ‘name’,
    options: { type: String }
    }
    我们来看看: 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);
    }
    }
    所以基本上这个对象将被存储到 properties TypeMetadataStorageHost 中的变量. TypeMetadataStorageHost是一个将存储大量这些对象的单例。
    3.模式生成
    了解如何 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,
    );
    }
    }
    最重要的部分是: const schemaDefinition = DefinitionsFactory.createForClass(target); .请注意,这里的目标是您的 Cat类(class)。
    你可以在这里看到方法定义: 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;
    }
    schemaMetadata.properties包含您在执行时存储的对象 TypeMetadataStorage.addPropertyMetadata :
    [
    {
    target: User,
    propertyKey: ‘name’,
    options: { type: String }
    }
    ]
    forEach将产生:
    {
    name: { type: String }
    }
    最后,它将被用作 mongoose.Schema 的参数。构造函数 mongoose/schema.factory.ts at master · nestjs/mongoose · GitHub :
    return new mongoose.Schema(
    schemaDefinition,
    schemaMetadata && schemaMetadata.options,
    );
    4.所以回答这个问题:
    你应该把什么作为 Prop()争论?
    还记得 Nest 何时执行 forEach生成 Mongoose 模式?
    schemaMetadata.properties?.forEach((item) => {
    const options = this.inspectTypeDefinition(item.options as any);
    schemaDefinition = {
    [item.propertyKey]: options as any,
    …schemaDefinition,
    };
    });
    获取 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;
    }
    在这里你可以得出这样的结论:
  • 如果optionsfunctionStringSchemaType它将直接返回并用作 Mongoose 选项。
  • 如果optionsArray ,它将返回该数组的第一个索引并将其包装在一个数组中。
  • 如果options不是 Arrayfunction ,例如,如果它只是一个普通的 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);
    至于如何添加引用来自 OwnerCat ,我们可以这样做:
    @Prop([{ type: MongooseSchema.Types.ObjectId, ref: Cat.name }])
    更新
    回答评论部分中的问题:
    如何在另一个模式中嵌入模式?
    如果你正确阅读了答案,你应该有足够的知识来做到这一点。但如果你没有,这里是 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);

    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);

    关于reference - Nest.js 中的 Mongoose 子文档,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62704600/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com