# @develop-x/nest-mongo

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

## 安装

```bash
npm install @develop-x/nest-mongo
```

## 主要特性

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

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

### 🛡️ **安全性和验证**
- **ID 验证**: 自动验证 ObjectId 格式
- **事务支持**: 支持 MongoDB 事务操作
- **错误处理**: 提供清晰的错误信息

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

## 配置

### 1. 导入模块

```typescript
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

```typescript
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

```typescript
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 }); // 默认值，可省略
  }
}
```

## 基础用法

### 常用操作示例

```typescript
@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 文档

### 基础查询方法

```typescript
// 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>
```

### 搜索和分析方法

```typescript
// 文本搜索
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[]>
```

### 创建方法

```typescript
// 单个创建
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>
```

### 更新方法

```typescript
// 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>
```

### 删除方法

```typescript
// 智能删除（根据配置决定软删除或硬删除）
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[]>
```

### 其他方法

```typescript
// 去重查询
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[]>
```

## 类型定义

### 配置选项

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

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

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

### 返回类型

```typescript
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 };
```

## 高级用法

### 事务支持

```typescript
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();
    }
  }
}
```

### 复杂查询示例

```typescript
// 文本搜索
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 迁移**:
   ```typescript
   // 旧版本
   export class User extends BaseSchema { ... }
   
   // 新版本（需要软删除）
   export class User extends SoftDeleteSchema { ... }
   
   // 新版本（不需要软删除）  
   export class Product extends BaseSchema { ... }
   ```

2. **Repository 迁移**:
   ```typescript
   // 旧版本
   constructor(model: Model<T>) {
     super(model);
   }
   
   // 新版本（启用软删除）
   constructor(model: Model<T>) {
     super(model, { useSoftDelete: true });
   }
   
   // 新版本（禁用软删除）
   constructor(model: Model<T>) {
     super(model, { useSoftDelete: false });
   }
   ```

3. **分页方法迁移**:
   ```typescript
   // 旧版本
   await repository.paginate(filter, page, pageSize);
   
   // 新版本
   await repository.paginate(filter, { page, pageSize, sort: { createdAt: -1 } });
   ```

## 性能优化建议

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

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

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

## 许可证

MIT

## 优势总结

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