import { Injectable } from '@nestjs/common';
import { InjectConnection } from '@nestjs/mongoose';
import { Connection } from 'mongoose';
import { IMongodbRepository } from '@domain/repositories/mongodb.repository.interface';
import { DatabaseName } from '@domain/value-objects/database-name.vo';
import { CollectionName } from '@domain/value-objects/collection-name.vo';
import { DocumentEntity } from '@domain/entities/document.entity';
import { CollectionEntity } from '@domain/entities/collection.entity';
import { LoggerService } from '@infrastructure/logging/logger.service';
@Injectable()
export class MongodbRepository implements IMongodbRepository {
constructor(
@InjectConnection() private connection: Connection,
private readonly logger: LoggerService,
) {
this.logger.setContext('MongodbRepository');
}
async find(
database: DatabaseName,
collection: CollectionName,
filter: Record<string, any> = {},
projection: Record<string, number> = {},
limit: number = 100,
sort: Record<string, number> = {},
): Promise<DocumentEntity[]> {
try {
const db = this.connection.useDb(database.getValue());
const coll = db.collection(collection.getValue());
const documents = await coll
.find(filter, { projection })
.limit(limit)
.sort(sort as any)
.toArray();
return documents.map(
(doc) =>
new DocumentEntity(
this.removeMongoId(doc),
doc._id?.toString(),
),
);
} catch (error) {
this.logger.error(`Error finding documents: ${error.message}`, error.stack);
throw error;
}
}
async insertOne(
database: DatabaseName,
collection: CollectionName,
document: Record<string, any>,
): Promise<DocumentEntity> {
try {
const db = this.connection.useDb(database.getValue());
const coll = db.collection(collection.getValue());
const result = await coll.insertOne(document);
return new DocumentEntity(
document,
result.insertedId?.toString(),
);
} catch (error) {
this.logger.error(`Error inserting document: ${error.message}`, error.stack);
throw error;
}
}
async insertMany(
database: DatabaseName,
collection: CollectionName,
documents: Record<string, any>[],
): Promise<DocumentEntity[]> {
try {
const db = this.connection.useDb(database.getValue());
const coll = db.collection(collection.getValue());
const result = await coll.insertMany(documents);
return documents.map((doc, index) =>
new DocumentEntity(
doc,
result.insertedIds[index]?.toString(),
),
);
} catch (error) {
this.logger.error(`Error inserting multiple documents: ${error.message}`, error.stack);
throw error;
}
}
async updateMany(
database: DatabaseName,
collection: CollectionName,
filter: Record<string, any>,
update: Record<string, any>,
): Promise<number> {
try {
const db = this.connection.useDb(database.getValue());
const coll = db.collection(collection.getValue());
const result = await coll.updateMany(filter, update);
return result.modifiedCount;
} catch (error) {
this.logger.error(`Error updating documents: ${error.message}`, error.stack);
throw error;
}
}
async deleteMany(
database: DatabaseName,
collection: CollectionName,
filter: Record<string, any>,
): Promise<number> {
try {
const db = this.connection.useDb(database.getValue());
const coll = db.collection(collection.getValue());
const result = await coll.deleteMany(filter);
return result.deletedCount;
} catch (error) {
this.logger.error(`Error deleting documents: ${error.message}`, error.stack);
throw error;
}
}
async count(
database: DatabaseName,
collection: CollectionName,
filter: Record<string, any> = {},
): Promise<number> {
try {
const db = this.connection.useDb(database.getValue());
const coll = db.collection(collection.getValue());
return await coll.countDocuments(filter);
} catch (error) {
this.logger.error(`Error counting documents: ${error.message}`, error.stack);
throw error;
}
}
async aggregate(
database: DatabaseName,
collection: CollectionName,
pipeline: any[],
): Promise<any[]> {
try {
const db = this.connection.useDb(database.getValue());
const coll = db.collection(collection.getValue());
return await coll.aggregate(pipeline).toArray();
} catch (error) {
this.logger.error(`Error executing aggregation: ${error.message}`, error.stack);
throw error;
}
}
async listCollections(database: DatabaseName): Promise<CollectionEntity[]> {
try {
const db = this.connection.useDb(database.getValue());
const collections = await db.listCollections();
return collections.map((coll: any) => new CollectionEntity(coll.name));
} catch (error: any) {
this.logger.error(`Error listing collections: ${error.message}`, error.stack);
throw error;
}
}
async listIndexes(database: DatabaseName, collection: CollectionName): Promise<any[]> {
try {
const db = this.connection.useDb(database.getValue());
const coll = db.collection(collection.getValue());
return await coll.listIndexes().toArray();
} catch (error) {
this.logger.error(`Error listing indexes: ${error.message}`, error.stack);
throw error;
}
}
async createIndex(
database: DatabaseName,
collection: CollectionName,
keys: Record<string, number>,
options: Record<string, any> = {},
): Promise<string> {
try {
const db = this.connection.useDb(database.getValue());
const coll = db.collection(collection.getValue());
return await coll.createIndex(keys, options);
} catch (error) {
this.logger.error(`Error creating index: ${error.message}`, error.stack);
throw error;
}
}
private removeMongoId(doc: any): Record<string, any> {
const { _id, ...rest } = doc;
return rest;
}
}