Now races and teamraces are paginated.

This commit is contained in:
BurnyLlama 2023-04-16 00:15:06 +02:00
parent 71ed4d8b70
commit 504c580dfd
6 changed files with 169 additions and 38 deletions

View File

@ -1,24 +1,34 @@
use rocket::{serde::json::Json, Route, State};
use crate::database::{models::race::Race, DatabaseHandler};
use crate::database::{
models::{
race::Race,
search_result::{get_page_number_as_i32_from_str, SearchResult},
},
DatabaseHandler,
};
#[get("/player/<player>")]
#[get("/player/<player>?<page>")]
async fn get_races_by_player(
db: &State<DatabaseHandler>,
player: &str,
) -> Result<Json<Vec<Race>>, String> {
match Race::get_races_by_player(db, player).await {
page: Option<&str>,
) -> Result<Json<SearchResult<Race>>, String> {
let page_num = get_page_number_as_i32_from_str(page);
match Race::get_races_by_player(db, player, page_num).await {
Ok(maps) => Ok(Json(maps)),
Err(err) => Err(format!("Error: {}", err)),
}
}
#[get("/map/<map>")]
#[get("/map/<map>?<page>")]
async fn get_races_by_map(
db: &State<DatabaseHandler>,
map: &str,
) -> Result<Json<Vec<Race>>, String> {
match Race::get_races_by_map(db, map).await {
page: Option<&str>,
) -> Result<Json<SearchResult<Race>>, String> {
let page_num = get_page_number_as_i32_from_str(page);
match Race::get_races_by_map(db, map, page_num).await {
Ok(maps) => Ok(Json(maps)),
Err(err) => Err(format!("Error: {}", err)),
}

View File

@ -1,24 +1,34 @@
use rocket::{serde::json::Json, Route, State};
use crate::database::{models::teamrace::Teamrace, DatabaseHandler};
use crate::database::{
models::{
search_result::{get_page_number_as_i32_from_str, SearchResult},
teamrace::Teamrace,
},
DatabaseHandler,
};
#[get("/player/<player>")]
#[get("/player/<player>?<page>")]
async fn get_teamrace_by_player(
db: &State<DatabaseHandler>,
player: &str,
) -> Result<Json<Vec<Teamrace>>, String> {
match Teamrace::get_teamrace_by_player(db, player).await {
page: Option<&str>,
) -> Result<Json<SearchResult<Teamrace>>, String> {
let page_num = get_page_number_as_i32_from_str(page);
match Teamrace::get_teamrace_by_player(db, player, page_num).await {
Ok(maps) => Ok(Json(maps)),
Err(err) => Err(format!("Error: {}", err)),
}
}
#[get("/map/<map>")]
#[get("/map/<map>?<page>")]
async fn get_teamrace_by_map(
db: &State<DatabaseHandler>,
map: &str,
) -> Result<Json<Vec<Teamrace>>, String> {
match Teamrace::get_teamrace_by_map(db, map).await {
page: Option<&str>,
) -> Result<Json<SearchResult<Teamrace>>, String> {
let page_num = get_page_number_as_i32_from_str(page);
match Teamrace::get_teamrace_by_map(db, map, page_num).await {
Ok(maps) => Ok(Json(maps)),
Err(err) => Err(format!("Error: {}", err)),
}

View File

@ -1,3 +1,4 @@
pub mod map;
pub mod race;
pub mod search_result;
pub mod teamrace;

View File

@ -3,6 +3,8 @@ use serde::{Deserialize, Serialize};
use crate::database::DatabaseHandler;
use super::search_result::{SearchResult, ROWS_PER_PAGE};
#[derive(Debug, Clone, sqlx::FromRow, Serialize, Deserialize)]
pub struct Race {
pub name: String,
@ -19,8 +21,9 @@ impl Race {
pub async fn get_races_by_player(
db: &DatabaseHandler,
player: &str,
) -> Result<Vec<Race>, sqlx::Error> {
sqlx::query_as!(
page: i32,
) -> Result<SearchResult<Race>, sqlx::Error> {
let results = sqlx::query_as!(
Race,
"
SELECT name,
@ -42,35 +45,73 @@ impl Race {
player
)
.fetch_all(&db.pool)
.await
.await?;
let total_pages = sqlx::query!(
"SELECT ceil(count(*) / $2) + 1 as total_pages FROM record_race WHERE name = $1",
player,
ROWS_PER_PAGE as i64
)
.fetch_one(&db.pool)
.await?
.total_pages
.map_or_else(|| 0, |as_i64| as_i64 as i32);
Ok(SearchResult {
results,
total_pages,
current_page: page,
})
}
pub async fn get_races_by_map(
db: &DatabaseHandler,
map: &str,
) -> Result<Vec<Race>, sqlx::Error> {
sqlx::query_as!(
page: i32,
) -> Result<SearchResult<Race>, sqlx::Error> {
let results = sqlx::query_as!(
Race,
"
SELECT name,
SELECT
name,
map,
time,
timestamp,
server,
ARRAY[cp1, cp2, cp3, cp4, cp5,
cp6, cp7, cp8, cp9, cp10,
cp11, cp12, cp13, cp14, cp15,
cp16, cp17, cp18, cp19, cp20,
cp21, cp22, cp23, cp24, cp25
cp6, cp7, cp8, cp9, cp10,
cp11, cp12, cp13, cp14, cp15,
cp16, cp17, cp18, cp19, cp20,
cp21, cp22, cp23, cp24, cp25
] AS checkpoints,
gameid,
ddnet7
FROM record_race WHERE map = $1
ORDER BY map, time
OFFSET (($2 - 1) * $3)
FETCH NEXT $3 ROWS ONLY
",
map
map,
page,
ROWS_PER_PAGE
)
.fetch_all(&db.pool)
.await
.await?;
let total_pages = sqlx::query!(
"SELECT ceil(count(*) / $2) + 1 as total_pages FROM record_race WHERE map = $1",
map,
ROWS_PER_PAGE as i64
)
.fetch_one(&db.pool)
.await?
.total_pages
.map_or_else(|| 0, |as_i64| as_i64 as i32);
Ok(SearchResult {
results,
total_pages,
current_page: page,
})
}
}

View File

@ -0,0 +1,16 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct SearchResult<T> {
pub results: Vec<T>,
pub current_page: i32,
pub total_pages: i32,
}
/// This constant should be used everywhere so that it can easily changed.
pub const ROWS_PER_PAGE: i32 = 200;
/// This gets an i32 from an Option<&str>. It is very fail safe of any bad value, where it will return 1 instead of an error.
pub fn get_page_number_as_i32_from_str(page_as_str: Option<&str>) -> i32 {
str::parse(page_as_str.unwrap_or("1")).unwrap_or(1)
}

View File

@ -1,7 +1,9 @@
use chrono::NaiveDateTime;
use serde::{Deserialize, Serialize};
use crate::database::DatabaseHandler;
use crate::database::{models::search_result::ROWS_PER_PAGE, DatabaseHandler};
use super::search_result::SearchResult;
#[derive(Debug, Clone, sqlx::FromRow, Serialize, Deserialize)]
pub struct Teamrace {
@ -15,25 +17,76 @@ impl Teamrace {
pub async fn get_teamrace_by_player(
db: &DatabaseHandler,
player: &str,
) -> Result<Vec<Teamrace>, sqlx::Error> {
sqlx::query_as!(
page: i32,
) -> Result<SearchResult<Teamrace>, sqlx::Error> {
let results = sqlx::query_as!(
Teamrace,
"SELECT players, map, time, timestamp FROM record_teamrace_array WHERE $1 = ANY(players)",
player
).fetch_all(&db.pool)
.await
"
SELECT players, map, time, timestamp
FROM record_teamrace_array
WHERE $1 = ANY(players)
OFFSET (($2 - 1) * $3)
FETCH NEXT $3 ROWS ONLY
",
player,
page,
ROWS_PER_PAGE
)
.fetch_all(&db.pool)
.await?;
let total_pages = sqlx::query!(
"SELECT ceil(count(*) / $2) + 1 as total_pages FROM record_teamrace_array WHERE $1 = ANY(players)",
player,
ROWS_PER_PAGE as i64
)
.fetch_one(&db.pool)
.await?
.total_pages
.map_or_else(|| 0, |as_i64| as_i64 as i32);
Ok(SearchResult {
results,
total_pages,
current_page: page,
})
}
pub async fn get_teamrace_by_map(
db: &DatabaseHandler,
map: &str,
) -> Result<Vec<Teamrace>, sqlx::Error> {
sqlx::query_as!(
page: i32,
) -> Result<SearchResult<Teamrace>, sqlx::Error> {
let results = sqlx::query_as!(
Teamrace,
"SELECT players, map, time, timestamp FROM record_teamrace_array WHERE map = $1",
map
"
SELECT players, map, time, timestamp
FROM record_teamrace_array
WHERE map = $1
OFFSET (($2 - 1) * $3)
FETCH NEXT $3 ROWS ONLY
",
map,
page,
ROWS_PER_PAGE
)
.fetch_all(&db.pool)
.await
.await?;
let total_pages = sqlx::query!(
"SELECT ceil(count(*) / $2) + 1 as total_pages FROM record_teamrace_array WHERE map = $1",
map,
ROWS_PER_PAGE as i64
)
.fetch_one(&db.pool)
.await?
.total_pages
.map_or_else(|| 0, |as_i64| as_i64 as i32);
Ok(SearchResult {
results,
total_pages,
current_page: page,
})
}
}