From 1520fd8d2f5a08fa6c577cb793f88df68acc05a0 Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Thu, 18 Dec 2025 17:55:19 -0800 Subject: [PATCH] optimize gw_scores endpoints with SQL aggregation --- .../api/v1/crew_memberships_controller.rb | 27 +++++++++---------- .../api/v1/phantom_players_controller.rb | 27 +++++++++---------- 2 files changed, 26 insertions(+), 28 deletions(-) diff --git a/app/controllers/api/v1/crew_memberships_controller.rb b/app/controllers/api/v1/crew_memberships_controller.rb index b919f29..f15f0d8 100644 --- a/app/controllers/api/v1/crew_memberships_controller.rb +++ b/app/controllers/api/v1/crew_memberships_controller.rb @@ -60,20 +60,19 @@ module Api # GET /crew/memberships/:id/gw_scores def gw_scores - # Group scores by participation/event and calculate totals - scores_by_event = @membership.gw_individual_scores - .includes(crew_gw_participation: :gw_event) - .group_by(&:crew_gw_participation) - - event_scores = scores_by_event.map do |participation, scores| - { - gw_event: GwEventBlueprint.render_as_hash(participation.gw_event), - total_score: scores.sum(&:score) - } - end - - # Sort by event number descending (most recent first) - event_scores.sort_by! { |es| -es[:gw_event][:event_number] } + # Use SQL GROUP BY and SUM for efficient aggregation + event_scores = GwIndividualScore + .joins(crew_gw_participation: :gw_event) + .where(crew_membership_id: @membership.id) + .group('gw_events.id, gw_events.event_number, gw_events.element, gw_events.start_date, gw_events.end_date') + .order('gw_events.event_number DESC') + .pluck('gw_events.id, gw_events.event_number, gw_events.element, gw_events.start_date, gw_events.end_date, SUM(gw_individual_scores.score)') + .map do |id, event_number, element, start_date, end_date, total_score| + { + gw_event: { id: id, event_number: event_number, element: element, start_date: start_date, end_date: end_date }, + total_score: total_score.to_i + } + end grand_total = event_scores.sum { |es| es[:total_score] } diff --git a/app/controllers/api/v1/phantom_players_controller.rb b/app/controllers/api/v1/phantom_players_controller.rb index c440ce9..57961a6 100644 --- a/app/controllers/api/v1/phantom_players_controller.rb +++ b/app/controllers/api/v1/phantom_players_controller.rb @@ -90,20 +90,19 @@ module Api # GET /crew/phantom_players/:id/gw_scores def gw_scores - # Group scores by participation/event and calculate totals - scores_by_event = @phantom.gw_individual_scores - .includes(crew_gw_participation: :gw_event) - .group_by(&:crew_gw_participation) - - event_scores = scores_by_event.map do |participation, scores| - { - gw_event: GwEventBlueprint.render_as_hash(participation.gw_event), - total_score: scores.sum(&:score) - } - end - - # Sort by event number descending (most recent first) - event_scores.sort_by! { |es| -es[:gw_event][:event_number] } + # Use SQL GROUP BY and SUM for efficient aggregation + event_scores = GwIndividualScore + .joins(crew_gw_participation: :gw_event) + .where(phantom_player_id: @phantom.id) + .group('gw_events.id, gw_events.event_number, gw_events.element, gw_events.start_date, gw_events.end_date') + .order('gw_events.event_number DESC') + .pluck('gw_events.id, gw_events.event_number, gw_events.element, gw_events.start_date, gw_events.end_date, SUM(gw_individual_scores.score)') + .map do |id, event_number, element, start_date, end_date, total_score| + { + gw_event: { id: id, event_number: event_number, element: element, start_date: start_date, end_date: end_date }, + total_score: total_score.to_i + } + end grand_total = event_scores.sum { |es| es[:total_score] }