@develop-x/nest-mongo
v1.0.8
Published
一个基于 NestJS 和 Mongoose 的 MongoDB 操作库,提供灵活的软删除功能和丰富的数据库操作方法。
Keywords
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 }迁移指南
从旧版本迁移
Schema 迁移:
// 旧版本 export class User extends BaseSchema { ... } // 新版本(需要软删除) export class User extends SoftDeleteSchema { ... } // 新版本(不需要软删除) export class Product extends BaseSchema { ... }Repository 迁移:
// 旧版本 constructor(model: Model<T>) { super(model); } // 新版本(启用软删除) constructor(model: Model<T>) { super(model, { useSoftDelete: true }); } // 新版本(禁用软删除) constructor(model: Model<T>) { super(model, { useSoftDelete: false }); }分页方法迁移:
// 旧版本 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 和详细的错误信息
