Untitled
unknown
plain_text
13 days ago
4.1 kB
2
Indexable
Never
import { Model, Document, FilterQuery, UpdateQuery, PopulateOptions } from 'mongoose'; import { createClient } from 'redis'; // Initialize Redis client const redisClient = createClient(); redisClient.on('error', (err) => console.error('Redis Client Error', err)); (async () => { await redisClient.connect(); })(); interface PaginationOptions { page?: number; limit?: number; } interface ReadAllOptions<T> { query?: FilterQuery<T>; projection?: Record<string, unknown>; sort?: Record<string, 1 | -1>; filter?: FilterQuery<T>; pagination?: PaginationOptions; includes?: string[] | PopulateOptions[]; } interface PaginatedResult<T> { docs: T[]; totalDocs: number; totalPages: number; currentPage: number; hasNextPage: boolean; hasPrevPage: boolean; nextPage: number | null; prevPage: number | null; } interface DbOperation<T extends Document> { create: (model: Model<T>, data: Partial<T>) => Promise<T>; read: (model: Model<T>, options: { query?: FilterQuery<T>; includes?: string[] | PopulateOptions[] }) => Promise<T | null>; readAll: (model: Model<T>, options: ReadAllOptions<T>) => Promise<PaginatedResult<T>>; update: (model: Model<T>, options: { query: FilterQuery<T>; update: UpdateQuery<T> }) => Promise<T | null>; delete: (model: Model<T>, options: { query: FilterQuery<T> }) => Promise<T | null>; invalidateCache: (keyPattern: string) => Promise<void>; } export const dbOperation: DbOperation<any> = { async create(model, data) { const newDocument = await model.create(data); await this.invalidateCache(`*${model.modelName}*`); return newDocument; }, async read(model, { query = {}, includes = [] }) { const result = await model.findOne(query).populate(includes).exec(); return result; }, async readAll(model, { query = {}, projection = {}, sort = {}, filter = {}, pagination = {}, includes = [] }) { const { page = 1, limit = 10 } = pagination; // Combine query and filter for better optimization const combinedQuery = { ...query, ...filter }; const cacheKey = `${model.modelName}:${JSON.stringify(combinedQuery)}:page:${page}:limit:${limit}:sort:${JSON.stringify(sort)}`; // Check cache const cachedData = await redisClient.get(cacheKey); if (cachedData) { console.log('Returning data from Redis cache'); return JSON.parse(cachedData); } // Total document count for pagination const totalDocs = await model.countDocuments(combinedQuery).exec(); const totalPages = Math.ceil(totalDocs / limit); const hasNextPage = page < totalPages; const hasPrevPage = page > 1; const nextPage = hasNextPage ? page + 1 : null; const prevPage = hasPrevPage ? page - 1 : null; // Query MongoDB const docs = await model.find(combinedQuery) .select(projection) .sort(sort) .skip((page - 1) * limit) .limit(limit) .populate(includes) .exec(); const result: PaginatedResult<any> = { docs, totalDocs, totalPages, currentPage: page, hasNextPage, hasPrevPage, nextPage, prevPage, }; // Cache the results await redisClient.set(cacheKey, JSON.stringify(result), { EX: 3600, // Cache expiration time (1 hour) }); console.log('Returning data from MongoDB and caching in Redis'); return result; }, async update(model, { query, update }) { const updatedDocument = await model.findOneAndUpdate(query, update, { new: true }).exec(); await this.invalidateCache(`*${model.modelName}*`); return updatedDocument; }, async delete(model, { query }) { const deletedDocument = await model.findOneAndDelete(query).exec(); await this.invalidateCache(`*${model.modelName}*`); return deletedDocument; }, async invalidateCache(keyPattern: string) { const keys = await redisClient.keys(keyPattern); if (keys.length > 0) { await redisClient.del(keys); console.log(`Invalidated cache for keys: ${keys}`); } }, }; (async () => { process.on('exit', async () => { await redisClient.disconnect(); }); })();
Leave a Comment