Fix N+1 query performance issue in schedule list endpoint

Replace inefficient loop with N database queries (1 + N schedules) with
optimized single query approach. Reduces database load and improves
response times for /api/schedule endpoint when fetching all schedules.

- Add ScheduleService::list_all_schedule_data_v1() method for single query
- Update handler to use optimized method instead of per-schedule queries
- Maintains identical API response format
- Scales better as schedule data grows
This commit is contained in:
Benjamin Slingo 2025-09-07 21:26:45 -04:00
parent e206ce3332
commit 73c1b416ee
2 changed files with 29 additions and 9 deletions

View file

@ -20,15 +20,8 @@ pub async fn get_schedule(
})))
},
None => {
// Return all schedules when no date specified
let schedules = ScheduleService::list_schedules_v1(&state.pool).await?;
let mut schedule_data_list = Vec::new();
for schedule in schedules {
let date_str = schedule.date.format("%Y-%m-%d").to_string();
let schedule_data = ScheduleService::get_schedule_data_v1(&state.pool, &date_str).await?;
schedule_data_list.push(schedule_data);
}
// Return all schedules when no date specified - single query optimization
let schedule_data_list = ScheduleService::list_all_schedule_data_v1(&state.pool).await?;
Ok(Json(serde_json::json!({
"success": true,

View file

@ -119,6 +119,33 @@ impl ScheduleService {
convert_schedules_to_v1(schedules)
}
/// Get all schedule data in ScheduleData format (optimized single query)
pub async fn list_all_schedule_data_v1(pool: &PgPool) -> Result<Vec<ScheduleData>> {
let schedules = schedule::list_all_schedules(pool).await?;
let mut schedule_data_list = Vec::new();
for schedule in schedules {
let personnel = Personnel {
ss_leader: schedule.ss_leader.unwrap_or_default(),
ss_teacher: schedule.ss_teacher.unwrap_or_default(),
mission_story: schedule.mission_story.unwrap_or_default(),
song_leader: schedule.song_leader.unwrap_or_default(),
announcements: schedule.scripture.unwrap_or_default(),
offering: schedule.offering.unwrap_or_default(),
special_music: schedule.special_music.unwrap_or_default(),
speaker: schedule.sermon_speaker.unwrap_or_default(),
deacons: schedule.deacons.unwrap_or_default(),
};
schedule_data_list.push(ScheduleData {
date: schedule.date.format("%Y-%m-%d").to_string(),
personnel,
});
}
Ok(schedule_data_list)
}
// V2 API methods (UTC timezone as per shared converter)
/// Get schedule by date with V2 format (UTC timestamps)