topic.repository.ts•5.08 kB
/**
* Topic repository implementation
*/
import {
Topic,
TopicCreateInput,
TopicOrderByWithRelationInput
} from '../models/prisma.types';
import { prisma } from '../utils/db';
import {
ISearchableRepository,
SearchOptions,
SearchResult
} from './base.repository';
import { logger } from '../utils/logger';
/**
* Repository for topic data access
*/
export class TopicRepository implements ISearchableRepository<Topic, number> {
/**
* Find topic by ID
* @param id Topic ID
* @returns Topic or null if not found
*/
async findById(id: number): Promise<Topic | null> {
try {
return await prisma.topic.findUnique({
where: { id }
});
} catch (error) {
logger.error('Error finding topic by ID:', error);
throw error;
}
}
/**
* Find all topics
* @returns Array of topics
*/
async findAll(): Promise<Topic[]> {
try {
return await prisma.topic.findMany();
} catch (error) {
logger.error('Error finding all topics:', error);
throw error;
}
}
/**
* Create a new topic
* @param data Topic data
* @returns Created topic
*/
async create(data: Omit<Topic, 'id' | 'createdAt' | 'updatedAt'>): Promise<Topic> {
try {
return await prisma.topic.create({
data: data as TopicCreateInput
});
} catch (error) {
logger.error('Error creating topic:', error);
throw error;
}
}
/**
* Update a topic
* @param id Topic ID
* @param data Updated topic data
* @returns Updated topic
*/
async update(id: number, data: Partial<Topic>): Promise<Topic> {
try {
return await prisma.topic.update({
where: { id },
data
});
} catch (error) {
logger.error('Error updating topic:', error);
throw error;
}
}
/**
* Delete a topic
* @param id Topic ID
* @returns True if deleted, false otherwise
*/
async delete(id: number): Promise<boolean> {
try {
await prisma.topic.delete({
where: { id }
});
return true;
} catch (error) {
logger.error('Error deleting topic:', error);
return false;
}
}
/**
* Search topics with filtering, sorting, and pagination
* @param options Search options
* @returns Search results with pagination metadata
*/
async search(options: SearchOptions): Promise<SearchResult<Topic>> {
const { skip = 0, take = 10, orderBy = { articleCount: 'desc' }, where = {} } = options;
const page = Math.floor(skip / take) + 1;
try {
// Get total count for pagination
const total = await prisma.topic.count({ where });
// Get results with pagination
const items = await prisma.topic.findMany({
skip,
take,
where,
orderBy: orderBy as TopicOrderByWithRelationInput
});
const totalPages = Math.ceil(total / take);
return {
items,
total,
page,
limit: take,
totalPages,
hasNext: page < totalPages,
hasPrevious: page > 1
};
} catch (error) {
logger.error('Error searching topics:', error);
throw error;
}
}
/**
* Find topic by name
* @param name Topic name
* @returns Topic or null if not found
*/
async findByName(name: string): Promise<Topic | null> {
try {
return await prisma.topic.findUnique({
where: { name }
});
} catch (error) {
logger.error('Error finding topic by name:', error);
throw error;
}
}
/**
* Find or create a topic by name
* @param name Topic name
* @returns Topic
*/
async findOrCreate(name: string): Promise<Topic> {
try {
const existingTopic = await this.findByName(name);
if (existingTopic) {
return existingTopic;
}
return await prisma.topic.create({
data: {
name,
articleCount: 1
}
});
} catch (error) {
logger.error('Error finding or creating topic:', error);
throw error;
}
}
/**
* Get topic with associated articles
* @param id Topic ID
* @returns Topic with articles or null if not found
*/
async getTopicWithArticles(id: number): Promise<any | null> {
try {
return await prisma.topic.findUnique({
where: { id },
include: {
articles: {
include: {
article: true
}
}
}
});
} catch (error) {
logger.error('Error getting topic with articles:', error);
throw error;
}
}
/**
* Increment article count for a topic
* @param id Topic ID
* @returns Updated topic
*/
async incrementArticleCount(id: number): Promise<Topic> {
try {
return await prisma.topic.update({
where: { id },
data: {
articleCount: {
increment: 1
}
}
});
} catch (error) {
logger.error('Error incrementing topic article count:', error);
throw error;
}
}
}