leaderboard: paginate API results
This commit is contained in:
parent
50cd57417d
commit
660fc1783e
2 changed files with 34 additions and 5 deletions
|
|
@ -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?;
|
||||
|
||||
|
|
|
|||
|
|
@ -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>,
|
||||
|
|
|
|||
Loading…
Reference in a new issue