npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@develop-x/nest-mongo

v1.0.8

Published

一个基于 NestJS 和 Mongoose 的 MongoDB 操作库,提供灵活的软删除功能和丰富的数据库操作方法。

Readme

@develop-x/nest-mongo

一个基于 NestJS 和 Mongoose 的 MongoDB 操作库,提供灵活的软删除功能和丰富的数据库操作方法。

安装

npm install @develop-x/nest-mongo

主要特性

🔄 灵活的软删除功能

  • 可选配置: 通过 RepositoryOptions 让软删除功能变为可选
  • 智能过滤: 启用软删除时自动过滤已删除数据
  • 完整操作: 支持软删除、恢复、硬删除等完整操作

🆔 虚拟字段 ID 支持

  • 查询优化: 查询始终使用高效的 _id 字段
  • 响应友好: 虚拟字段 id 提供字符串格式的友好访问
  • 自动转换: Schema 配置自动处理 ID 序列化

🛡️ 安全性和验证

  • ID 验证: 自动验证 ObjectId 格式
  • 事务支持: 支持 MongoDB 事务操作
  • 错误处理: 提供清晰的错误信息

🔍 强大的查询功能

  • 文本搜索: 多字段模糊搜索
  • 分页查询: 完整的分页信息和排序支持
  • 聚合操作: 支持复杂的聚合查询
  • 统计分析: 内置数据统计和重复检查

配置

1. 导入模块

import { MongoModule } from '@develop-x/nest-mongo';
import { ConfigModule, ConfigService } from '@nestjs/config';

@Module({
  imports: [
    MongoModule.forRootAsync({
      imports: [ConfigModule],
      inject: [ConfigService],
      useFactory: (configService: ConfigService) => ({
        uri: configService.get<string>('MONGODB_URI'),
      }),
    }),
  ],
})
export class AppModule {}

2. 创建 Schema

import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { BaseSchema, SoftDeleteSchema } from '@develop-x/nest-mongo';

// 不需要软删除的实体
@Schema({ timestamps: true, versionKey: false, toJSON: { virtuals: true } })
export class Product extends BaseSchema {
  @Prop({ required: true })
  name: string;

  @Prop({ required: true })
  price: number;
}

// 需要软删除的实体
@Schema({ timestamps: true, versionKey: false, toJSON: { virtuals: true } })
export class User extends SoftDeleteSchema {
  @Prop({ required: true })
  name: string;

  @Prop({ required: true, unique: true })
  email: string;
}

export type ProductDocument = Product & Document;
export type UserDocument = User & Document;
export const ProductSchema = SchemaFactory.createForClass(Product);
export const UserSchema = SchemaFactory.createForClass(User);

3. 创建 Repository

import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { BaseRepository } from '@develop-x/nest-mongo';

// 不使用软删除的 Repository
@Injectable()
export class ProductRepository extends BaseRepository<ProductDocument> {
  constructor(@InjectModel('Product') productModel: Model<ProductDocument>) {
    super(productModel, { useSoftDelete: false });
  }
}

// 使用软删除的 Repository
@Injectable()
export class UserRepository extends BaseRepository<UserDocument> {
  constructor(@InjectModel('User') userModel: Model<UserDocument>) {
    super(userModel, { useSoftDelete: true }); // 默认值,可省略
  }
}

基础用法

常用操作示例

@Injectable()
export class UserService {
  constructor(
    private readonly userRepository: UserRepository,
    private readonly productRepository: ProductRepository,
  ) {}

  // 创建用户(支持事务)
  async createUser(userData: Partial<User>, session?: ClientSession) {
    return this.userRepository.create(userData, session);
  }

  // 查找用户
  async findUserById(id: string) {
    return this.userRepository.findById(id); // 自动过滤软删除
  }

  // 搜索用户
  async searchUsers(query: string) {
    return this.userRepository.search({
      fields: ['name', 'email'],
      query,
      caseSensitive: false
    });
  }

  // 分页查询
  async getUserList(page: number, pageSize: number) {
    return this.userRepository.paginate({}, {
      page,
      pageSize,
      sort: { createdAt: -1 }
    });
  }

  // 软删除用户
  async deleteUser(id: string) {
    return this.userRepository.softDeleteById(id);
  }

  // 获取统计信息
  async getUserStats() {
    return this.userRepository.getStats();
  }

  // 产品操作(无软删除)
  async createProduct(productData: Partial<Product>) {
    return this.productRepository.create(productData);
  }

  async deleteProduct(id: string) {
    return this.productRepository.removeById(id); // 直接物理删除
  }
}

API 文档

基础查询方法

// ID 查询
findById(id: string | Types.ObjectId): Promise<T | null>
findByIds(ids: (string | Types.ObjectId)[]): Promise<T[]>

// 条件查询
find(filter?: FilterQuery<T>, projection?: any, options?: QueryOptions): Promise<T[]>
findOne(filter?: FilterQuery<T>, projection?: any, options?: QueryOptions): Promise<T | null>
findWithSort(filter?: FilterQuery<T>, sort?: Record<string, 1 | -1>): Promise<T[]>

// 计数和存在性检查
count(filter?: FilterQuery<T>): Promise<number>
exists(filter?: FilterQuery<T>): Promise<boolean>

搜索和分析方法

// 文本搜索
search(searchOptions: SearchOptions, filter?: FilterQuery<T>): Promise<T[]>

// 分页查询(增强版)
paginate(
  filter: FilterQuery<T>,
  paginationOptions: PaginationOptions,
  projection?: any,
  options?: QueryOptions
): Promise<PaginationResult<T>>

// 聚合查询
aggregate(pipeline: any[], session?: ClientSession): Promise<any[]>
groupBy(groupField: string, filter?: FilterQuery<T>, countField?: string): Promise<any[]>

// 随机查询
findRandom(filter?: FilterQuery<T>, limit?: number): Promise<T[]>

// 统计信息
getStats(filter?: FilterQuery<T>): Promise<{total: number; active: number; deleted: number}>
findDuplicates(field: string): Promise<any[]>

创建方法

// 单个创建
create(doc: Partial<T>, session?: ClientSession): Promise<T>

// 批量创建
createMany(docs: Partial<T>[], session?: ClientSession): Promise<T[]>

// 查找或创建
findOrCreate(filter: FilterQuery<T>, docIfNew?: Partial<T>): Promise<T>

更新方法

// ID 更新
updateById(
  id: string | Types.ObjectId,
  update: UpdateQuery<T>,
  options?: UpdateOptions & QueryOptions
): Promise<T | null>

// 条件更新
updateOne(filter: FilterQuery<T>, update: UpdateQuery<T>, options?: UpdateOptions & QueryOptions): Promise<UpdateResult | null>
updateMany(filter: FilterQuery<T>, update: UpdateQuery<T>, options?: UpdateOptions & QueryOptions): Promise<UpdateResult | null>
updateManyByIds(ids: (string | Types.ObjectId)[], update: UpdateQuery<T>, options?: UpdateOptions & QueryOptions): Promise<UpdateResult | null>

// 查找并更新
findOneAndUpdate(filter: FilterQuery<T>, update: UpdateQuery<T>, options?: QueryOptions): Promise<T | null>

// 更新或插入
upsert(filter: FilterQuery<T>, update: UpdateQuery<T>, docIfNew?: Partial<T>): Promise<T | null>

// 数值操作
incrementById(id: string | Types.ObjectId, field: string, value?: number): Promise<T | null>
decrementById(id: string | Types.ObjectId, field: string, value?: number): Promise<T | null>
incrementMany(filter: FilterQuery<T>, field: string, value?: number): Promise<UpdateResult | null>

删除方法

// 智能删除(根据配置决定软删除或硬删除)
removeById(id: string | Types.ObjectId): Promise<T | null>
removeMany(filter: FilterQuery<T>): Promise<SoftDeleteResult | HardDeleteResult>

// 软删除(仅当启用软删除时可用)
softDeleteById(id: string | Types.ObjectId): Promise<T | null>
softDeleteMany(filter: FilterQuery<T>): Promise<UpdateResult | null>

// 恢复操作
restoreById(id: string | Types.ObjectId): Promise<T | null>
restoreMany(filter: FilterQuery<T>): Promise<UpdateResult | null>

// 硬删除(物理删除)
hardDeleteById(id: string | Types.ObjectId): Promise<T | null>
hardDeleteMany(filter: FilterQuery<T>): Promise<DeleteResult | null>

// 查找已删除的数据
findDeleted(filter?: FilterQuery<T>, projection?: any, options?: QueryOptions): Promise<T[]>

其他方法

// 去重查询
distinct(field: string, filter?: FilterQuery<T>): Promise<any[]>

// 批量操作
bulkWrite(ops: any[], session?: ClientSession): Promise<any>

// 地理位置查询
findNear(coordinates: [number, number], maxDistance?: number, filter?: FilterQuery<T>): Promise<T[]>

// 获取所有数据(包括已删除的)
findAll(filter?: FilterQuery<T>, projection?: any, options?: QueryOptions): Promise<T[]>

类型定义

配置选项

interface RepositoryOptions {
  useSoftDelete?: boolean; // 是否启用软删除,默认 true
}

interface PaginationOptions {
  page: number;
  pageSize: number;
  sort?: Record<string, 1 | -1>;
}

interface SearchOptions {
  fields: string[];
  query: string;
  caseSensitive?: boolean;
}

返回类型

interface PaginationResult<T> {
  items: T[];
  total: number;
  page: number;
  pageSize: number;
  totalPages: number;
  hasNext: boolean;
  hasPrev: boolean;
}

type SoftDeleteResult = { type: 'soft'; result: UpdateResult | null };
type HardDeleteResult = { type: 'hard'; result: DeleteResult | null };

高级用法

事务支持

import { InjectConnection } from '@nestjs/mongoose';
import { Connection } from 'mongoose';

@Injectable()
export class UserService {
  constructor(
    @InjectConnection() private connection: Connection,
    private userRepository: UserRepository,
  ) {}

  async createUserWithProfile(userData: any, profileData: any) {
    const session = await this.connection.startSession();
    
    try {
      session.startTransaction();
      
      const user = await this.userRepository.create(userData, session);
      const profile = await this.profileRepository.create({
        ...profileData,
        userId: user._id
      }, session);
      
      await session.commitTransaction();
      return { user, profile };
    } catch (error) {
      await session.abortTransaction();
      throw error;
    } finally {
      session.endSession();
    }
  }
}

复杂查询示例

// 文本搜索
const users = await userRepository.search({
  fields: ['name', 'email', 'description'],
  query: 'john',
  caseSensitive: false
}, { role: 'user' });

// 分页查询
const result = await userRepository.paginate(
  { role: 'user' },
  { page: 1, pageSize: 10, sort: { createdAt: -1 } }
);

// 聚合统计
const roleStats = await userRepository.groupBy('role');

// 获取随机用户
const randomUsers = await userRepository.findRandom({ isActive: true }, 5);

// 统计信息
const stats = await userRepository.getStats({ role: 'user' });
// 返回: { total: 150, active: 120, deleted: 30 }

迁移指南

从旧版本迁移

  1. Schema 迁移:

    // 旧版本
    export class User extends BaseSchema { ... }
       
    // 新版本(需要软删除)
    export class User extends SoftDeleteSchema { ... }
       
    // 新版本(不需要软删除)  
    export class Product extends BaseSchema { ... }
  2. Repository 迁移:

    // 旧版本
    constructor(model: Model<T>) {
      super(model);
    }
       
    // 新版本(启用软删除)
    constructor(model: Model<T>) {
      super(model, { useSoftDelete: true });
    }
       
    // 新版本(禁用软删除)
    constructor(model: Model<T>) {
      super(model, { useSoftDelete: false });
    }
  3. 分页方法迁移:

    // 旧版本
    await repository.paginate(filter, page, pageSize);
       
    // 新版本
    await repository.paginate(filter, { page, pageSize, sort: { createdAt: -1 } });

性能优化建议

1. 合理使用索引

// 在 Schema 中添加索引
@Schema({ 
  timestamps: true, 
  versionKey: false, 
  toJSON: { virtuals: true },
  indexes: [
    { email: 1 },
    { name: 'text' }, // 文本搜索索引
    { createdAt: -1 }
  ]
})

2. 使用投影优化查询

// 只查询需要的字段
const users = await userRepository.find(
  { role: 'user' },
  { name: 1, email: 1 }, // 只返回 name 和 email
  { lean: true } // 返回普通对象而非 Mongoose 文档
);

3. 批量操作

// 批量更新
const operations = users.map(user => ({
  updateOne: {
    filter: { _id: user._id },
    update: { $set: { lastLogin: new Date() } }
  }
}));
await userRepository.bulkWrite(operations);

许可证

MIT

优势总结

灵活性: 可以选择是否使用软删除功能
向后兼容: 默认启用软删除,保持原有行为
类型安全: 完整的 TypeScript 类型支持
性能优化: 不需要软删除时避免不必要的查询条件
功能丰富: 搜索、分页、统计、事务等完整功能
清晰的 API: 明确区分软删除和硬删除操作
开发友好: 提供虚拟字段 ID 和详细的错误信息