啥是东莞网站优化推广,企业获客方式,简单 网站设计,长沙营销企业网站建设在 NestJS 中链接 MongoDB 有两种方法。一种方法就是使用TypeORM来进行连接#xff0c;另外一种方法就是使用Mongoose。
此笔记主要是记录使用Mongoose的。所以我们先安装所需的依赖#xff1a;
npm i nestjs/mongoose mongoose安装完成后#xff0c;需要在AppModule中引入…在 NestJS 中链接 MongoDB 有两种方法。一种方法就是使用TypeORM来进行连接另外一种方法就是使用Mongoose。
此笔记主要是记录使用Mongoose的。所以我们先安装所需的依赖
npm i nestjs/mongoose mongoose安装完成后需要在AppModule中引入MongooseModule。具体实例如下
import databaseConfig from ./config/database.config;
import { MongooseModule } from nestjs/mongoose;Module({imports: [MongooseModule.forRoot(mongodb://localhost:27017/managementsytem),CommodityModule,],
})
export class AppModule {}MongooseModule.forRoot中的配置对象与mongoose.connect()的配置对象一致。
模型注入
在 Mongoose 中最为核心的是模式因为每个模式都会转换成一个具体的MongoDB集合并且模式定义了集合中文档的结构。
在 Mongoose 中模型主要是负责从底层创建数据和读取文档。
在 NestJs 中模式可以使用装饰器来创建也可以使用 Mongoose 本身手动创建。使用装饰器创建的模式在代码量和代码可读性上都比 Mongoose 本身手动创建的要好。所以建议使用装饰器来创建模式。具体的实例如下
import { HydratedDocument } from mongoose;
import { Prop, Schema, SchemaFactory } from nestjs/mongoose;export type CommodityDocument HydratedDocumentCommodity;Schema()
export class Commodity {Prop()name: string; // 商品名称Prop()price: number; // 商品价格Prop()stock: number; // 商品库存Prop([String])tag: string; // 商品标签
}export const CommoditySchema SchemaFactory.createForClass(Commodity);注意 您还可以使用 DefinitionsFactory 类生成原始模式定义。这允许您手动修改根据您提供的元数据生成的架构定义。当模型的字段可能会进行扩展和删除的时候我们就可以使用DefinitionsFactory来维护模式的数据。
Schema()装饰器的作用主要是定义模式。在上述的例子中会把Commodity类映射到同名的MongoDB集合中但是需要注意的是在 MongoDB 集合中的名称会添加一个’s’即Commoditys。此装饰器可以接受一个可选参数此参数主要是用于设置 MongoDB 的模型相关参数具体的内容可以进入这里查看。
Prop() 装饰器定义文档中的属性。例如在上面的模式定义中我们定义了三个属性名称、价格和标签。借助 TypeScript 元数据和反射功能可以自动推断这些属性的架构类型。但是在无法隐式反映类型的更复杂场景例如数组或嵌套对象结构中必须显式指示类型如下所示
Prop([String])
tags: string[];或者Prop() 装饰器接受选项对象参数阅读有关可用选项的更多信息。这样您可以指示属性是否是必需的、指定默认值或将其标记为不可变。例如
Prop({ required: true })
name: string;如果一个模型中带有另外一个属性的话可以使用Prop()装饰器。例如Commodity 里面包含一个 supply 模型这样我们需要在 Commodity 里面添加具体的类和引用。具体例子如下
import * as mongoose from mongoose;
import { Supply } from ../owners/schemas/supply.schema;Prop({ type: mongoose.Schema.Types.ObjectId, ref: Supply })
supply: Supply;如果有多个提供商您的属性配置应如下所示
Prop({ type: [{ type: mongoose.Schema.Types.ObjectId, ref: Supply }] })
supply: Supply[];最后原始模式定义也可以传递给装饰器。例如当属性表示未定义为类的嵌套对象时这很有用。为此请使用 nestjs/mongoose 包中的 raw() 函数如下所示
Prop(raw({firstName: { type: String },lastName: { type: String }
}))
details: Recordstring, any;或者如果您不想使用装饰器则可以手动定义架构。例如:
export const CommoditySchema new mongoose.Schema({name: String,price: Number,stock: Number,tags: [String],
});当我们定义好模式后就可以在对应的Module中进行定义具体实例如下
Module({imports: [ConfigModule,MongooseModule.forFeature([{ name: Commodity.name, schema: CommoditySchema },]),],controllers: [CommodityController],providers: [CommodityService],
})
export class CommodityModule {}MongooseModule 提供了 forFeature() 方法来配置模块包括定义应在当前范围内注册哪些模型。如果您还想在另一个模块中使用模型的话你只要在 CommodityModule 中添加 MongooseModule 作为导出部分并在另一个模块中导入 CommodityModule 就可以。
注册模式后您可以使用 InjectModel() 装饰器将 Commodity 模型注入到 CommodityService 中
import { Injectable } from nestjs/common;
import { InjectModel } from nestjs/mongoose;
import { Model } from mongoose;
import { Commodity } from src/schemas/commodity.schemas;Injectable()
export class CommodityService {constructor(InjectModel(Commodity.name) private commodityModel: ModelCommodity) {}create(commodity: Commodity) {const createdCommdity new this.commodityModel(commodity);return createdCommdity.save();}
}获取 Mongoose Connection 对象
有时您可能需要访问本机 Mongoose Connection 对象。例如您可能希望对连接对象进行本机 API 调用。您可以使用 InjectConnection() 装饰器注入 Mongoose Connection如下所示
import { Injectable } from nestjs/common;
import { InjectConnection } from nestjs/mongoose;
import { Connection } from mongoose;Injectable()
export class CatsService {constructor(InjectConnection() private connection: Connection) {}
}多个数据库使用
有些项目需要多个数据库连接。这也可以通过该模块来实现。要使用多个连接请首先创建连接。在这种情况下连接命名就成为强制性的。具体实例如下
import { Module } from nestjs/common;
import { CommodityModule } from ./module/commodity.module;
import { AccountModule } from ./module/account.module;
import { ConfigModule } from nestjs/config;
import configuration from ./config/configuration;
import databaseConfig from ./config/database.config;
import { MongooseModule } from nestjs/mongoose;Module({imports: [ConfigModule.forRoot({load: [configuration, databaseConfig],cache: true,}),MongooseModule.forRoot(mongodb://localhost:27017/managementsytem, {connectionName: commodity,}),MongooseModule.forRoot(mongodb://localhost:27018/user, {connectionName: user,}),CommodityModule,AccountModule,],
})
export class AppModule {}**注意**您不应有多个没有名称或名称相同的连接否则它们将被覆盖。
假如你设置了多个数据链接后你必须在对应的模块中使用MongooseModule.forFeature()函数来声明链接哪个数据库。具体的例子如下
Module({imports: [ConfigModule,MongooseModule.forFeature([{ name: Commodity.name, schema: CommoditySchema }],commodity),],controllers: [CommodityController],providers: [CommodityService],exports: [CommodityService],
})
export class CommodityModule {}如果您在 AppModule 中声明了多个数据库并在对应的模块中声明了链接的数据库名称那么我们的提供者就需要在装饰器中添加第二个参数。具体实例如下
Injectable()
export class CatsService {constructor(InjectModel(Commodity.name, commodities)private commodityModel: ModelCommodity) {}async create(commodity: Commodity) {const createdCommdity new this.commodityModel(commodity);const _res await createdCommdity.save();return _res;}
}您还可以为给定连接注入连接
import { Injectable } from nestjs/common;
import { InjectConnection } from nestjs/mongoose;
import { Connection } from mongoose;Injectable()
export class CommodityService {constructor(InjectConnection(commodity) private connection: Connection) {}
}要将给定连接注入到自定义提供程序例如工厂提供程序请使用 getConnectionToken() 函数将连接名称作为参数传递。
{provide: CommodityService,useFactory: (commodityConnection: Connection) {return new CommodityService(commodityConnection);},inject: [getConnectionToken(commodity)],
}Mongo 钩子中间件
Mongo 钩子的使用场景一般都是在编写自己的插件时才会使用。
Mongo 的中间件是在模式级别指定的对于编写插件很有用。编译模型后调用 pre() 或 post() 在 Mongoose 中不起作用。要在模型注册之前注册钩子请使用 MongooseModule 的 forFeatureAsync() 方法以及工厂提供程序即 useFactory。通过这种技术您可以访问模式对象然后使用 pre() 或 post() 方法在该模式上注册挂钩。具体实例如下
Module({imports: [MongooseModule.forFeatureAsync([{name: Commodity.name,useFactory: () {const schema CommoditySchema;schema.pre(save, function () {console.log(Hello from pre save);});return schema;},},]),],
})
export class AppModule {}与其他工厂提供者一样我们的工厂函数可以是异步的并且可以通过注入注入依赖项。
Module({imports: [MongooseModule.forFeatureAsync([{name: Commodity.name,imports: [ConfigModule],useFactory: (configService: ConfigService) {const schema CommoditySchema;schema.pre(save, function() {console.log(${configService.get(APP_NAME)}: Hello from pre save,),});return schema;},inject: [ConfigService],},]),],
})
export class AppModule {}中间件中使用插件
要为给定架构注册插件请使用 forFeatureAsync() 方法。
Module({imports: [MongooseModule.forFeatureAsync([{name: Commodity.name,useFactory: () {const schema CommoditySchema;schema.plugin(require(mongoose-autopopulate));return schema;},},]),],
})
export class AppModule {}要一次为所有模式注册插件请调用 Connection 对象的 .plugin() 方法。您应该在创建模型之前访问连接为此请使用连接工厂
import { Module } from nestjs/common;
import { MongooseModule } from nestjs/mongoose;Module({imports: [MongooseModule.forRoot(mongodb://localhost/test, {connectionFactory: (connection) {connection.plugin(require(mongoose-autopopulate));return connection;},}),],
})
export class AppModule {}鉴别器
鉴别器是一种模式继承机制。它们使您能够在同一底层 MongoDB 集合之上拥有具有重叠架构的多个模型。这个功能相当于模型形成继承关系。
假设您想在单个集合中跟踪不同类型的事件。每个事件都会有一个时间戳。
Schema({ discriminatorKey: kind })
export class Event {Prop({type: String,required: true,enum: [ClickedLinkEvent.name, SignUpEvent.name],})kind: string;Prop({ type: Date, required: true })time: Date;
}export const EventSchema SchemaFactory.createForClass(Event);SignedUpEvent 和 ClickedLinkEvent 实例将与通用事件存储在同一集合中。
现在让我们定义 ClickedLinkEvent 类如下所示
Schema()
export class ClickedLinkEvent {kind: string;time: Date;Prop({ type: String, required: true })url: string;
}export const ClickedLinkEventSchema SchemaFactory.createForClass(ClickedLinkEvent);和 SignUpEvent 类
Schema()
export class SignUpEvent {kind: string;time: Date;Prop({ type: String, required: true })user: string;
}export const SignUpEventSchema SchemaFactory.createForClass(SignUpEvent);完成此操作后使用鉴别器选项为给定模式注册鉴别器。它适用于 MongooseModule.forFeature 和 MongooseModule.forFeatureAsync
import { Module } from nestjs/common;
import { MongooseModule } from nestjs/mongoose;Module({imports: [MongooseModule.forFeature([{name: Event.name,schema: EventSchema,discriminators: [{ name: ClickedLinkEvent.name, schema: ClickedLinkEventSchema },{ name: SignUpEvent.name, schema: SignUpEventSchema },],},]),],
})
export class EventsModule {}完成上述的代码后其实模型之间的关系如下 异步配置
当您需要异步而不是静态地传递模块选项时请使用 forRootAsync() 方法。与大多数动态模块一样Nest 提供了多种处理异步配置的技术。
一种技术是使用工厂函数
MongooseModule.forRootAsync({useFactory: () ({uri: mongodb://localhost/nest,}),
});与其他工厂提供者一样我们的工厂函数可以是异步的并且可以通过注入注入依赖项。
MongooseModule.forRootAsync({imports: [ConfigModule],useFactory: async (configService: ConfigService) ({uri: configService.getstring(MONGODB_URI),}),inject: [ConfigService],
});或者您可以使用类而不是工厂来配置 MongooseModule如下所示
MongooseModule.forRootAsync({useClass: MongooseConfigService,
});上面的构造在 MongooseModule 中实例化 MongooseConfigService使用它来创建所需的选项对象。请注意在此示例中MongooseConfigService 必须实现 MongooseOptionsFactory 接口如下所示。MongooseModule 将在所提供的类的实例化对象上调用 createMongooseOptions() 方法。
Injectable()
export class MongooseConfigService implements MongooseOptionsFactory {createMongooseOptions(): MongooseModuleOptions {return {uri: mongodb://localhost/nest,};}
}如果您想重用现有的选项提供程序而不是在 MongooseModule 内创建私有副本请使用 useExisting 语法。
MongooseModule.forRootAsync({imports: [ConfigModule],useExisting: ConfigService,
});