from motor.motor_asyncio import AsyncIOMotorClient, AsyncIOMotorDatabase import redis.asyncio as aioredis from app.config import get_settings class Database: client: AsyncIOMotorClient | None = None db: AsyncIOMotorDatabase | None = None redis: aioredis.Redis | None = None db = Database() def get_database() -> AsyncIOMotorDatabase: """MongoDB 데이터베이스 인스턴스를 반환한다. 주의: `if not db.db:` 사용 금지 (pymongo 4.9.x NotImplementedError) """ if db.db is None: raise RuntimeError("Database not initialized") return db.db def get_redis() -> aioredis.Redis: """Redis 클라이언트 인스턴스를 반환한다.""" if db.redis is None: raise RuntimeError("Redis not initialized") return db.redis async def connect_db(): """MongoDB + Redis 연결 초기화""" settings = get_settings() # MongoDB db.client = AsyncIOMotorClient(settings.mongodb_url) db.db = db.client[settings.mongodb_database] # 인덱스 생성 await create_indexes(db.db) # Redis db.redis = aioredis.from_url( settings.redis_url, encoding="utf-8", decode_responses=True, ) async def disconnect_db(): """연결 종료""" if db.client is not None: db.client.close() if db.redis is not None: await db.redis.close() async def create_indexes(database: AsyncIOMotorDatabase): """컬렉션 인덱스 생성""" todos = database["todos"] categories = database["categories"] # todos 텍스트 검색 인덱스 await todos.create_index( [("title", "text"), ("content", "text"), ("tags", "text")], name="text_search_index", weights={"title": 10, "content": 5, "tags": 3}, ) # todos 복합 인덱스 await todos.create_index( [("category_id", 1), ("created_at", -1)], name="category_created", ) await todos.create_index( [("completed", 1), ("created_at", -1)], name="completed_created", ) await todos.create_index( [("priority", 1), ("created_at", -1)], name="priority_created", ) await todos.create_index( [("tags", 1)], name="tags", ) await todos.create_index( [("due_date", 1)], name="due_date", ) await todos.create_index( [("completed", 1), ("due_date", 1)], name="completed_due_date", ) # categories 인덱스 await categories.create_index("name", unique=True, name="category_name_unique") await categories.create_index("order", name="category_order")