Nestjs-mongo-自增id插件

843 阅读3分钟
前沿

我们知道,MongoDB为每个文档默认设置了_id字段作为主键,默认是ObjectId类型的。可以通过配置改变_id的值类型,但_id字段是必不可少的。当我们想要为主键设置成数字自增类型,就比较困难,因为Mongoose好像没有提供直接的属性自增设置。

方案思路

要实现属性(包括主键及其他属性)自增效果,方案也不复杂,就需要自己维护一个记数表,来记录需要自增的属性属性当前的值

github上搜到了很多现成的插件,选择一个并集成到Nestjs中,就是这个:自增id插件

另外,如果还不清楚如何在Nest中使用Mongoose,请看我这篇文章Nestjs-集成MongoDB数据库

分析插件

先看一下这个插件的实现思路吧,把插件代码下载下来,核心源码就一个文件auto-increment.ts

nest-id_inc-1.jpg

下面是插件初始化的方法,接收一个数据库的连接对象,通过此对象完成数据库连接,并为记数表的schema和model赋值:

nest-id_inc-2.jpg

接下来是plugin()方法的定义,通过plugin()方法可以设置我们要为数据库中的哪个集合中的哪个字段,设置自增。

nest-id_inc-3.jpg

实现自增的核心是通过pre()钩子函数去监听每次保存文档,出发回调函数,执行自增逻辑:

nest-id_inc-4.jpg

自增逻辑中,需要考虑两种情况,

一种是外部的保存文档操作,为自增属性设置了指定值,这时候就不能用上面配置的插件参数递增宽度来更新记数表中的自增属性值

nest-id_inc-5.jpg

另一种就是外部的保存文档操作没有设置指定值,这时候就需要用插件配置的递增宽度来更新记数表中的自增属性值

nest-id_inc-6.jpg

到这里,插件就分析完了,思路就是这么个思路。

集成到MongoDB

接下来就把这个插件集成到Nest中的MongoDB配置中,让我们的数据库支持属性自增。

看插件用法,首先是要调用initialize方法初始化,这一步的目的就是为我们的数据库中创建记数表。数据库连接是在AppModule中配置的,问题是如何才能获取到connection对象呢?

这就要看MongooseModule.forRootAsync方法支持哪些参数了,通过查看@nest/mongoose源码,useFactory方法返回的MongooseModuleFactoryOptions对象继承自MongooseModuleOptions,而MongooseModuleOptions定义如下:

export interface MongooseModuleOptions extends ConnectOptions, Record<string, any> {
    uri?: string; // 这就是数据库连接uri
    retryAttempts?: number;
    retryDelay?: number;
    connectionName?: string;
    connectionFactory?: (connection: any, name: string) => any; // 通过这个回调函数就可以拿到connection
    connectionErrorFactory?: (error: MongooseError) => MongooseError;
}

所以,修改数据库连接的配置,如下:

@Module({
  imports: [
    MongooseModule.forRootAsync({
      imports: [ConfigModule],
      useFactory: async (configService: ConfigService) => ({
        uri: configService.get<string>('DBConnection'),
        // 拿到connection
        connectionFactory: (connection) => {
          AutoIncrementInit(connection) // 属性自增插件初始化
          return connection;
        }
      }),
      inject: [ConfigService]
    }),
  ],
  controllers: [],
  providers: [],
})
export class AppModule {
  constructor() { }
}

插件初始化完成,最后就是对自增属性进行设置了,数据库集合的Schema也是在Module中设置的,比如UserModule中:

// user.module.ts
@Module({
  imports: [
    // 不使用插件可以是这样注入
    MongooseModule.forFeature([
      { name: User.name, schema: UserSchema }
    ]
  ],
  controllers: [UserController],
  providers: [UserService],
})
export class UserModule {}

需要对schema进行操作,就需要使用forFeatureAsync()方法以及工厂函数useFactory(),跟使用钩子函数一样,如下:

// user.module.ts
@Module({
  imports: [
    MongooseModule.forFeatureAsync([
      // 没有插件,没有钩子函数
      {
        name: User.name,
        useFactory: () => UserSchema,
      },
      // 使用自增插件,自定义插件配置
      {
        name: User.name,
        useFactory: () => {
          UserSchema.plugin(autoIncrementPlugin, {
            model: 'users',
            field: 'count',
            startAt: 0,
            incrementBy: 1,
            unique: true
          });
          return UserSchema;
        },
      },
      // 使用自增插件,使用默认配置(_id自增)
      {
        name: User.name,
        useFactory: () => {
          UserSchema.plugin(autoIncrementPlugin, 'users');
          return UserSchema;
        },
      },
    ])
  ],
  controllers: [UserController],
  providers: [UserService],
})
export class UserModule { }

到这里,自增插件就配置完成,可以使用了!

总结

以上内容官网均有涉及,浅浅记录,不值一提。如有错误,欢迎指正!!!