Now races and teamraces are paginated.

main
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 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( async fn get_races_by_player(
db: &State<DatabaseHandler>, db: &State<DatabaseHandler>,
player: &str, player: &str,
) -> Result<Json<Vec<Race>>, String> { page: Option<&str>,
match Race::get_races_by_player(db, player).await { ) -> 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)), Ok(maps) => Ok(Json(maps)),
Err(err) => Err(format!("Error: {}", err)), Err(err) => Err(format!("Error: {}", err)),
} }
} }
#[get("/map/<map>")] #[get("/map/<map>?<page>")]
async fn get_races_by_map( async fn get_races_by_map(
db: &State<DatabaseHandler>, db: &State<DatabaseHandler>,
map: &str, map: &str,
) -> Result<Json<Vec<Race>>, String> { page: Option<&str>,
match Race::get_races_by_map(db, map).await { ) -> 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)), Ok(maps) => Ok(Json(maps)),
Err(err) => Err(format!("Error: {}", err)), Err(err) => Err(format!("Error: {}", err)),
} }

View File

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

View File

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

View File

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