3030
3131async def _update_video_aggregate_rating (
3232 video_id : VideoID ,
33- ratings_db_table : AstraDBCollection ,
34- videos_db_table : AstraDBCollection ,
33+ new_rating : int ,
34+ old_rating : int | None = None ,
35+ summary_db_table : AstraDBCollection | None = None ,
3536) -> None :
36- """Recalculate average and total ratings count for the given video."""
37+ """Increment counters on the video_ratings summary table.
3738
38- cursor = ratings_db_table .find (
39- filter = {"videoid" : str (video_id )}, projection = {"rating" : 1 }
40- )
41- docs : List [Dict [str , Any ]] = (
42- await cursor .to_list () if hasattr (cursor , "to_list" ) else cursor
43- )
39+ * **New rating** (old_rating is None): increment rating_counter by 1 and
40+ rating_total by new_rating.
41+ * **Updated rating** (old_rating provided): increment rating_total by
42+ (new_rating - old_rating) only — counter stays the same.
43+ """
44+
45+ if summary_db_table is None :
46+ summary_db_table = await get_table (RATINGS_SUMMARY_TABLE_NAME )
47+
48+ vid_str = str (video_id )
4449
45- if docs :
46- values = [int (d ["rating" ]) for d in docs if "rating" in d ]
47- total = len (values )
48- average = sum (values ) / total if total else None
50+ if old_rating is None :
51+ inc_doc : Dict [str , Any ] = {"rating_counter" : 1 , "rating_total" : new_rating }
4952 else :
50- total = 0
51- average = None
53+ delta = new_rating - old_rating
54+ inc_doc = { "rating_total" : delta }
5255
5356 try :
54- await videos_db_table .update_one (
55- filter = {"videoid" : str (video_id )},
56- update = {
57- "$set" : {
58- "averageRating" : average ,
59- "totalRatingsCount" : total ,
60- "updatedAt" : datetime .now (timezone .utc ),
61- }
62- },
57+ await summary_db_table .update_one (
58+ filter = {"videoid" : vid_str },
59+ update = {"$inc" : inc_doc },
60+ upsert = True ,
6361 )
6462 except DataAPIResponseException as exc :
65- # If the videos table schema does not include these columns (common
66- # when running against the default KillrVideo schema) Astra will
67- # reject the update with UNKNOWN_TABLE_COLUMNS. That is not fatal –
68- # the API can still compute aggregates on-the-fly.
69- if "UNKNOWN_TABLE_COLUMNS" not in str (exc ):
63+ if "Update operation not supported" in str (
64+ exc
65+ ) or "unsupported operations" in str (exc ):
66+ existing = await summary_db_table .find_one (
67+ filter = {"videoid" : vid_str }
68+ )
69+ counter = int (existing .get ("rating_counter" , 0 )) if existing else 0
70+ total = int (existing .get ("rating_total" , 0 )) if existing else 0
71+ if old_rating is None :
72+ counter += 1
73+ total += new_rating
74+ else :
75+ total += new_rating - old_rating
76+ await summary_db_table .update_one (
77+ filter = {"videoid" : vid_str },
78+ update = {"$set" : {"rating_counter" : counter , "rating_total" : total }},
79+ upsert = True ,
80+ )
81+ else :
7082 raise
71- # Otherwise silently ignore so the rating operation succeeds.
7283
7384
7485async def rate_video (
@@ -152,9 +163,12 @@ async def rate_video(
152163 "user_activity insert failed for rate; ignoring" , exc_info = True
153164 )
154165
155- # update aggregate
166+ # update aggregate counters on the summary table
167+ old_rating_value : int | None = None
168+ if existing_doc :
169+ old_rating_value = int (existing_doc ["rating" ])
156170 await _update_video_aggregate_rating (
157- video_id , db_table , await get_table ( video_service . VIDEOS_TABLE_NAME )
171+ video_id , new_rating = request . rating , old_rating = old_rating_value
158172 )
159173 return rating_obj
160174
@@ -168,19 +182,35 @@ async def get_video_ratings_summary(
168182 video_id : VideoID ,
169183 current_user_id : UUID | None = None ,
170184 ratings_db_table : Optional [AstraDBCollection ] = None ,
185+ summary_db_table : Optional [AstraDBCollection ] = None ,
171186) -> AggregateRatingResponse :
172187 """Return aggregated rating info for a video and optionally the caller's rating."""
173188
174- # Fetch video to access pre-computed aggregates
189+ # 404 check – make sure the video exists
175190 target_video = await video_service .get_video_by_id (video_id )
176191 if target_video is None :
177192 raise HTTPException (
178193 status_code = status .HTTP_404_NOT_FOUND , detail = "Video not found"
179194 )
180195
181- avg = target_video .averageRating
182- total = target_video .totalRatingsCount
196+ # Read counters from the video_ratings summary table
197+ if summary_db_table is None :
198+ summary_db_table = await get_table (RATINGS_SUMMARY_TABLE_NAME )
199+
200+ summary_doc = await summary_db_table .find_one (
201+ filter = {"videoid" : str (video_id )}
202+ )
203+
204+ if summary_doc :
205+ rating_counter = int (summary_doc .get ("rating_counter" , 0 ))
206+ rating_total = int (summary_doc .get ("rating_total" , 0 ))
207+ avg = round (rating_total / rating_counter , 2 ) if rating_counter > 0 else None
208+ total = rating_counter
209+ else :
210+ avg = None
211+ total = 0
183212
213+ # Look up current user's individual rating
184214 user_rating_value : RatingValue | None = None
185215 if current_user_id is not None :
186216 if ratings_db_table is None :
0 commit comments