1+ from contextlib import asynccontextmanager
12from fastapi import FastAPI
23from fastapi .middleware .cors import CORSMiddleware
34from src .routers import movies
45from src .utils .errorHandler import register_error_handlers
56from src .database .mongo_client import db , get_collection
6- import traceback
7+
78import os
89from dotenv import load_dotenv
910
1011# Load environment variables from .env file
1112load_dotenv ()
1213
13- app = FastAPI ()
1414
15- # Add CORS middleware
16- cors_origins = os .getenv ("CORS_ORIGINS" , "http://localhost:3000,http://localhost:3001" ).split ("," )
17- app .add_middleware (
18- CORSMiddleware ,
19- allow_origins = [origin .strip () for origin in cors_origins ], # Load from environment variable
20- allow_credentials = True ,
21- allow_methods = ["*" ],
22- allow_headers = ["*" ],
23- )
24-
25- register_error_handlers (app )
26- app .include_router (movies .router , prefix = "/api/movies" , tags = ["movies" ])
15+ @asynccontextmanager
16+ async def lifespan (app : FastAPI ):
17+ # Startup: Create search indexes
18+ await ensure_search_index ()
19+ await vector_search_index ()
20+ yield
21+ # Shutdown: Clean up resources if needed
22+ # Add any cleanup code here
2723
2824
29- @app .on_event ("startup" )
30- async def initialize_database_indexes ():
25+ async def ensure_search_index ():
3126 try :
3227 movies_collection = db .get_collection ("movies" )
3328 comments_collection = db .get_collection ("comments" )
@@ -37,70 +32,43 @@ async def initialize_database_indexes():
3732 indexes = [idx async for idx in result ]
3833 index_names = [index ["name" ] for index in indexes ]
3934 if "movieSearchIndex" in index_names :
40- print ("MongoDB Search index already exists." )
41- else :
42- # Create a mapping if the movieSearchIndex does not exist
43- index_definition = {
44- "mappings" : {
45- "dynamic" : False ,
46- "fields" : {
47- "plot" : {"type" : "string" , "analyzer" : "lucene.standard" },
48- "fullplot" : {"type" : "string" , "analyzer" : "lucene.standard" },
49- "directors" : {"type" : "string" , "analyzer" : "lucene.standard" },
50- "writers" : {"type" : "string" , "analyzer" : "lucene.standard" },
51- "cast" : {"type" : "string" , "analyzer" : "lucene.standard" }
52- }
35+ return
36+
37+ # Create a mapping if the movieSearchIndex does not exist
38+ index_definition = {
39+ "mappings" : {
40+ "dynamic" : False ,
41+ "fields" : {
42+ "plot" : {"type" : "string" , "analyzer" : "lucene.standard" },
43+ "fullplot" : {"type" : "string" , "analyzer" : "lucene.standard" },
44+ "directors" : {"type" : "string" , "analyzer" : "lucene.standard" },
45+ "writers" : {"type" : "string" , "analyzer" : "lucene.standard" },
46+ "cast" : {"type" : "string" , "analyzer" : "lucene.standard" }
5347 }
5448 }
55- # Creates movieSearchIndex on the movies collection
56- await db .command ({
57- "createSearchIndexes" : "movies" ,
58- "indexes" : [{
59- "name" : "movieSearchIndex" ,
60- "definition" : index_definition
61- }]
62- })
63- print ("MongoDB Search index created." )
64-
65- # Check and create index on movie_id field in comments collection
66- # This index will significantly improve $lookup performance in aggregations
67- cursor = await comments_collection .list_indexes ()
68- existing_indexes = await cursor .to_list (length = None )
69- movie_id_index_exists = any (
70- "movie_id" in index .get ("key" , {}) for index in existing_indexes
71- )
72-
73- if not movie_id_index_exists :
74- # Create index on movie_id field for better aggregation performance
75- await comments_collection .create_index ("movie_id" )
76- print ("Index on 'movie_id' field in comments collection created." )
77- else :
78- print ("Index on 'movie_id' field in comments collection already exists." )
79-
80- # Also create a compound index on movie_id and date for even better performance
81- # when sorting comments by date within each movie
82- compound_index_exists = any (
83- index .get ("key" , {}).get ("movie_id" ) == 1 and index .get ("key" , {}).get ("date" ) == - 1
84- for index in existing_indexes
85- )
86-
87- if not compound_index_exists :
88- await comments_collection .create_index ([("movie_id" , 1 ), ("date" , - 1 )])
89- print ("Compound index on 'movie_id' and 'date' fields in comments collection created." )
90- else :
91- print ("Compound index on 'movie_id' and 'date' fields already exists." )
92-
49+ }
50+ # Creates movieSearchIndex on the movies collection
51+ await db .command ({
52+ "createSearchIndexes" : "movies" ,
53+ "indexes" : [{
54+ "name" : "movieSearchIndex" ,
55+ "definition" : index_definition
56+ }]
57+ })
9358 except Exception as e :
94- print (f"Error creating indexes: { e } " )
59+ raise RuntimeError (
60+ f"Failed to create search index 'movieSearchIndex': { str (e )} . "
61+ f"Search functionality may not work properly. "
62+ f"Please check your MongoDB Atlas configuration and ensure the cluster supports search indexes."
63+ )
64+
9565
96- @app .on_event ("startup" )
9766async def vector_search_index ():
9867 """
9968 Creates vector search index on application startup if it doesn't already exist.
10069 This ensures the index is ready before any vector search requests are made.
10170 """
10271 try :
103-
10472 embedded_movies_collection = get_collection ("embedded_movies" )
10573
10674 # Get list of existing indexes - convert AsyncCommandCursor to list
@@ -128,10 +96,29 @@ async def vector_search_index():
12896 }
12997
13098 # Create the index
131- result = await embedded_movies_collection .create_search_index (index_definition )
132- print ("Vector search index 'vector_index' ready to query." )
99+ await embedded_movies_collection .create_search_index (index_definition )
133100
134101 except Exception as e :
135- print (f"Error during vector search index setup: { str (e )} " )
136- print (f"Error type: { type (e ).__name__ } " )
102+ raise RuntimeError (
103+ f"Failed to create vector search index 'vector_index': { str (e )} . "
104+ f"Vector search functionality will not be available. "
105+ f"Please check your MongoDB Atlas configuration, ensure the cluster supports vector search, "
106+ f"and verify the 'embedded_movies' collection exists with the required embedding field."
107+ )
108+
109+
110+ app = FastAPI (lifespan = lifespan )
111+
112+ # Add CORS middleware
113+ cors_origins = os .getenv ("CORS_ORIGINS" , "http://localhost:3000,http://localhost:3001" ).split ("," )
114+ app .add_middleware (
115+ CORSMiddleware ,
116+ allow_origins = [origin .strip () for origin in cors_origins ], # Load from environment variable
117+ allow_credentials = True ,
118+ allow_methods = ["*" ],
119+ allow_headers = ["*" ],
120+ )
121+
122+ register_error_handlers (app )
123+ app .include_router (movies .router , prefix = "/api/movies" , tags = ["movies" ])
137124
0 commit comments