leaderboard: paginate API results

This commit is contained in:
Patch Generator 2026-08-13 10:37:16 +00:00 committed by Jonas Rabenstein
commit 660fc1783e
2 changed files with 34 additions and 5 deletions

View file

@ -10,21 +10,39 @@ use sqlx::PgPool;
use crate::{
AppState,
model::{ActivityCrossing, LeaderboardGroup, LeaderboardRow},
model::{ActivityCrossing, LeaderboardGroup, LeaderboardPage, LeaderboardRow},
};
#[derive(Debug, Deserialize, Default)]
pub struct LeaderboardQuery {
pub group_id: Option<i64>,
#[serde(default)]
pub offset: i64,
#[serde(default = "default_page_size")]
pub limit: i64,
}
fn default_page_size() -> i64 {
50
}
pub async fn api(
State(state): State<AppState>,
Query(query): Query<LeaderboardQuery>,
) -> Result<Json<Vec<LeaderboardGroup>>, (axum::http::StatusCode, String)> {
build(&state.db, query.group_id)
) -> Result<Json<LeaderboardPage>, (axum::http::StatusCode, String)> {
let offset = query.offset.max(0);
let limit = query.limit.clamp(1, 200);
build(&state.db, query.group_id, offset, limit)
.await
.map(Json)
.map(|groups| {
let returned = groups.iter().map(|group| group.rows.len() as i64).sum::<i64>();
Json(LeaderboardPage {
groups,
next_offset: offset + returned,
has_more: returned == limit,
})
})
.map_err(|error| {
tracing::error!(%error, "leaderboard query failed");
(
@ -34,7 +52,7 @@ pub async fn api(
})
}
pub async fn build(db: &PgPool, group_id: Option<i64>) -> Result<Vec<LeaderboardGroup>> {
pub async fn build(db: &PgPool, group_id: Option<i64>, offset: i64, limit: i64) -> Result<Vec<LeaderboardGroup>> {
let rows = sqlx::query(
r#"
WITH bucketed AS (
@ -114,9 +132,13 @@ pub async fn build(db: &PgPool, group_id: Option<i64>) -> Result<Vec<Leaderboard
to_county,
crossing_time,
rank
LIMIT $2
OFFSET $3
"#,
)
.bind(group_id)
.bind(limit)
.bind(offset)
.fetch_all(db)
.await?;

View file

@ -63,6 +63,13 @@ pub struct LeaderboardGroup {
pub rows: Vec<LeaderboardRow>,
}
#[derive(Debug, Serialize, Clone)]
pub struct LeaderboardPage {
pub groups: Vec<LeaderboardGroup>,
pub next_offset: i64,
pub has_more: bool,
}
#[derive(Debug, Serialize, Clone)]
pub struct ActivityCrossing {
pub crossing_time: DateTime<Utc>,