NestJs 中使用 mongoose

在 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 = HydratedDocument<Commodity>;@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: Record<string, 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: Model<Commodity>) {}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: Model<Commodity>) {}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.get<string>("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,
});

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/39297.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SpringBoot后端服务开启Https协议提供访问(使用阿里云资源)

目录 概述 申请/下载证书 部署证书 本地测试访问 服务器部署访问 最后/扩展 总结 概述 本篇博客说明如何将SpringBoot项目开启Https协议提供访问。 博文以步骤【申请/下载证书】&#xff0c;【部署证书】&#xff0c;【本地测试访问】&#xff0c;【服务器部署访问】 &a…

LORA开发板采集温湿度数据,连接PC上位机显示和液晶屏显示

一、准备材料 准备以下板子和器件 Lora开发板x2 USB数据线x2 OLED 屏幕x2 StLink下载器x1 母对母杜邦线x3 DHT11 x2 二、设备连接 如图所示先将OLED 屏幕插入到开发板中 接着按照图中所示的&#xff0c;将串口一以及lora的拨码开关拨到指定方向 接着将USB数据线一端插入到…

(七)Unity VR项目升级至Vision Pro需要做的工作

Vision Pro 概述 定位为混合现实眼镜&#xff0c;对AR支持更友好 无手柄&#xff0c;支持手&#xff08;手势&#xff09;、眼&#xff08;注视&#xff09;、语音交互 支持空间音频&#xff0c;相比立体声、环绕声更有沉浸感和空间感 支持VR/AR应用&#xff0c;支持多种应用模…

FPGA应用学习笔记-----复位电路(二)和小结

不可复位触发器若和可复位触发器混合写的话&#xff0c;不可复位触发器是由可复位触发器馈电的。 不应该出现的复位&#xff0c;因为延时导致了冒险&#xff0c;异步复位存在静态冒险 附加素隐含项&#xff0c;利用数电方法&#xff0c;消除静态冒险 这样多时钟区域还是算异步的…

深度学习实战基础案例——卷积神经网络(CNN)基于SqueezeNet的眼疾识别|第1例

文章目录 前言一、数据准备1.1 数据集介绍1.2 数据集文件结构 二、项目实战2.1 数据标签划分2.2 数据预处理2.3 构建模型2.4 开始训练2.5 结果可视化 三、数据集个体预测 前言 SqueezeNet是一种轻量且高效的CNN模型&#xff0c;它参数比AlexNet少50倍&#xff0c;但模型性能&a…

Linkedin为什么要退出中国市场?

在迅速发展的时代,职场也在不断变换,只有不断地提升专业技能和进行培训,才能在职场中获得成功。Linkedin作为一家专注于职业发展的平台,专业的学习体验以及热门技能赢得了人们青睐。然而遗憾的是这个曾经让人备受青睐的平台,如今却在中国市场中黯然落幕,究竟是何种原因让曾经风…

大数据Flink(六十一):Flink流处理程序流程和项目准备

文章目录 Flink流处理程序流程和项目准备 一、Flink流处理程序的一般流程

Spark SQL优化:NOT IN子查询优化解决

背景 有如下的数据查询场景。 SELECT a,b,c,d,e,f FROM xxx.BBBB WHERE dt ${zdt.addDay(0).format(yyyy-MM-dd)} AND predict_type not IN ( SELECT distinct a FROM xxx.AAAAAWHERE dt ${zdt.addDay(0).format(yyyy-MM-dd)} ) 分析 通过查看SQL语句的执行计划基本…

Dubbo基础学习(笔记一)

目录 第一章、概念介绍1.1&#xff09;什么是RPC框架1.2&#xff09;什么是分布式系统1.3&#xff09;Dubbo概述1.3&#xff09;Dubbo基本架构 第二章、服务提供者2.1&#xff09;目录结构和依赖2.2&#xff09;model层2.3&#xff09;service层2.4&#xff09;resources配置文…

ARTS 挑战打卡的第8天 ---volatile 关键字在MCU中的作用,四个实例讲解(Tips)

前言 &#xff08;1&#xff09;volatile 关键字作为嵌入式面试的常考点&#xff0c;很多人都不是很了解&#xff0c;或者说一知半解。 &#xff08;2&#xff09;可能有些人会说了&#xff0c;volatile 关键字不就是防止编译器优化的吗&#xff1f;有啥好详细讲解的&#xff1…

澎峰科技|邀您关注2023 RISC-V中国峰会!

峰会概览 2023 RISC-V中国峰会&#xff08;RISC-V Summit China 2023&#xff09;将于8月23日至25日在北京香格里拉饭店举行。本届峰会将以“RISC-V生态共建”为主题&#xff0c;结合当下全球新形势&#xff0c;把握全球新时机&#xff0c;呈现RISC-V全球新观点、新趋势。 本…

《3D 数学基础》12 几何图元

目录 1 表达图元的方法 1.1 隐式表示法 1.2 参数表示 1.3 直接表示 2. 直线和射线 2.1 射线的不同表示法 2.1.1 两点表示 2.1.2 参数表示 2.1.3 相互转换 2.2 直线的不同表示法 2.2.1 隐式表示法 2.2.2 斜截式 2.2.3 相互转换 3. 球 3.1 隐式表示 1 表达图元的方…

C语言的使用技巧--在IO操作中的移位和快速配置

在WB32F103&#xff08;ARM cortex m3内核&#xff0c;96Mhz&#xff09;的gpio初始化中有一段代码&#xff0c;充分的结合了硬件特征并使用C语言的技巧来快速的配置对应的GPIO的功能&#xff0c;堪称经典和楷模&#xff0c;代码异常简洁&#xff0c;执行速度快&#xff0c;配置…

Python pycparser(c文件解析)模块使用教程

文章目录 安装 pycparser 模块模块开发者网址获取抽象语法树1. 需要导入的模块2. 获取 不关注预处理相关 c语言文件的抽象语法树ast3. 获取 预处理后的c语言文件的抽象语法树ast 语法树组成1. 数据类型定义 Typedef2. 类型声明 TypeDecl3. 标识符类型 IdentifierType4. 变量声明…

语聚AI公测发布,大语言模型时代下新的生产力工具

语聚AI 公测发布 距离语聚AI内测上线已经过去近1个月。 这期间&#xff0c;我们共邀请了近百位资深用户与行业专家加入语聚AI产品体验。通过大家的热情参与积极反馈&#xff0c;我们不断优化并完善了语聚AI的功能与使用体验。 经过研发团队不懈的努力&#xff0c;今天语聚AI终…

梅赛德斯-奔驰将成为首家集成ChatGPT的汽车制造商

ChatGPT的受欢迎程度毋庸置疑。OpenAI这个基于人工智能的工具&#xff0c;每天能够吸引无数用户使用&#xff0c;已成为当下很受欢迎的技术热点。因此&#xff0c;有许多公司都在想方设法利用ChatGPT来提高产品吸引力&#xff0c;卖点以及性能。在汽车领域&#xff0c;梅赛德斯…

代码随想录算法训练营第59天|动态规划part16|583. 两个字符串的删除操作、72. 编辑距离、编辑距离总结篇

代码随想录算法训练营第59天&#xff5c;动态规划part16&#xff5c;583. 两个字符串的删除操作、72. 编辑距离、编辑距离总结篇 583. 两个字符串的删除操作 583. 两个字符串的删除操作 思路&#xff1a; 思路见代码 代码&#xff1a; python class Solution(object):de…

2023年国赛数学建模思路 - 案例:FPTree-频繁模式树算法

文章目录 算法介绍FP树表示法构建FP树实现代码 建模资料 ## 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 算法介绍 FP-Tree算法全称是FrequentPattern Tree算法&#xff0c;就是频繁模式树算法&#xff0c…

QT-Mysql数据库图形化接口

QT sql mysqloper.h qsqlrelationaltablemodelview.h /************************************************************************* 接口描述&#xff1a;Mysql数据库图形化接口 拟制&#xff1a; 接口版本&#xff1a;V1.0 时间&#xff1a;20230727 说明&#xff1a;支…

基于VUE3+Layui从头搭建通用后台管理系统(前端篇)九:自定义组件封装下

一、本章内容 续上一张,本章实现一些自定义组件的封装,包括文件上传组件封装、级联选择组件封装、富文本组件封装等。 1. 详细课程地址: 待发布 2. 源码下载地址: 待发布 二、界面预览 三、开发视频 基于VUE3+Layui从头搭建通用后台管