1

假设我有一个events由 TypeGraphql 和 Typegoose 创建的简单集合,它存储如下对象:

{ _id: ObjectId(...), name: 'SomeEvent', category: ObjectId('...') }

和相应的类型:

@ObjectType()
export class Event {
  @Field(() => ID)
  _id!: Types.ObjectId

  @prop({ ref: 'Category' })
  @Field(() => Category)
  category!: Ref<Category>

  @prop()
  @Field()
  name!: string
}

我也categories有现在只包含_id和的集合name

现在我想将一些事件插入数据库。是否可以自动检查输入中提供的 categoryId 是否存在于集合中categories,如果不存在,则抛出错误?现在,可以在category字段中添加任何事件,然后当我尝试通过查询获取它时,它会抛出一个category无法解决的错误,因为没有具有此 ID 的类别。我知道,我可以在添加事件期间手动检查它,但如果我有更多这样的字段,那将是有问题的。

4

2 回答 2

3

在 Martin Devillers 回答的帮助下,我能够编写一个验证器来使用带有 typegoose 的类验证器来验证引用的文档。

这是我的 refdoc.validator.ts:

import { ValidationArguments, ValidatorConstraint, ValidatorConstraintInterface } from "class-validator";
import { Injectable } from "@nestjs/common";
import { getModelForClass } from "@typegoose/typegoose";


@ValidatorConstraint({ name: "RefDoc", async: true })
@Injectable()
export class RefDocValidator implements ValidatorConstraintInterface {

    async validate(refId: string, args: ValidationArguments) {
        const modelClass = args.constraints[0];
        return getModelForClass(modelClass).exists({ _id: refId })
    }

    defaultMessage(): string {
        return "Referenced Document not found!";
    }
}

然后我可以使用@Validate-Decorator 将它应用到 DTO 或模型上。我传入的参数是 typegoose 模型。

@Validate(RefDocValidator, [Costcenter])
costcenterId: string;

似乎对我有用,我愿意接受任何改进..

编辑:使用自定义装饰器会更好,正如 Martin Devlers 建议的那样:

refdoc.validator.ts

import { registerDecorator, ValidationArguments, ValidationOptions, ValidatorConstraint, ValidatorConstraintInterface } from "class-validator";
import { Injectable } from "@nestjs/common";
import { getModelForClass } from "@typegoose/typegoose";


@ValidatorConstraint({ name: "RefDoc", async: true })
@Injectable()
export class RefDocValidator implements ValidatorConstraintInterface {

    async validate(refId: string, args: ValidationArguments) {
        const modelClass = args.constraints[0];
        return getModelForClass(modelClass).exists({ _id: refId })
    }

    defaultMessage(): string {
        return "Referenced Document not found!";
    }
}

export function RefDocExists(modelClass: any, validationOptions?: ValidationOptions) {
    return function (object: Object, propertyName: string) {
        registerDecorator({
            name: 'RefDocExists',
            target: object.constructor,
            propertyName: propertyName,
            constraints: [modelClass],
            options: validationOptions,
            validator: RefDocValidator,
        });
    };
}

然后您可以在 DTO 上使用它,例如:

@ApiProperty()
@IsNotEmpty()
//@Validate(RefDocValidator, [Costcenter]) old
@RefDocExists(Costcenter) //new
costcenterId: string;
于 2021-03-17T10:22:55.357 回答
0

开箱即用,MongoDB、mongoose 和 typegoose 都没有提供任何自动参照完整性检查。

在数据库级别,此功能不存在(这也是 MongoDB 和 SQL Server/Oracle 等数据库之间的根本区别之一)。

但是,在应用程序级别,您可以通过多种方式实现此行为:

  1. 中间件。Mongoose 中间件允许您向模型添加通用行为。如果您在EventModel代码库的不同位置插入文档并且不想重复验证逻辑,这将非常有用。例如:
EventModel.path('category').validate(async (value, respond) => {
    const categoryExists = await CategoryModel.exists({ _id: value })
    respond(categoryExists)
})
  1. 猫鼬插件。像mongoose-id-validator这样的插件允许您将上述行为添加到所有模式中的任何类型的文档引用中。
  2. 手动。可能是最不喜欢的选项,但为了完整起见,我提到它:使用猫鼬的Model.exists()您可以使用单线来实现这一点:const categoryExists = await CategoryModel.exists({ _id: event.category })

重申一下:以上所有选项都是权宜之计。无论如何,某人总是有可能直接进入您的数据库并破坏参照完整性。

于 2021-03-17T02:57:55.503 回答