implement ListBotMatches, allow querying matches by bot/opponent pair
This commit is contained in:
parent
ce07419bbc
commit
2278ecd258
4 changed files with 110 additions and 23 deletions
86
planetwars-server/src/db/match_queries.rs
Normal file
86
planetwars-server/src/db/match_queries.rs
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
use super::matches::BotMatchOutcome;
|
||||||
|
use crate::schema::matches;
|
||||||
|
use chrono::NaiveDateTime;
|
||||||
|
use diesel::pg::Pg;
|
||||||
|
use diesel::query_builder::{AstPass, Query, QueryFragment, QueryId};
|
||||||
|
use diesel::sql_types::*;
|
||||||
|
use diesel::{PgConnection, QueryResult, RunQueryDsl};
|
||||||
|
|
||||||
|
pub struct ListBotMatches {
|
||||||
|
pub bot_id: i32,
|
||||||
|
pub opponent_id: Option<i32>,
|
||||||
|
pub outcome: Option<BotMatchOutcome>,
|
||||||
|
|
||||||
|
pub before: Option<NaiveDateTime>,
|
||||||
|
pub after: Option<NaiveDateTime>,
|
||||||
|
pub amount: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl QueryFragment<Pg> for ListBotMatches {
|
||||||
|
fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Pg>) -> QueryResult<()> {
|
||||||
|
out.unsafe_to_cache_prepared();
|
||||||
|
|
||||||
|
out.push_sql("SELECT matches.* FROM matches");
|
||||||
|
out.push_sql(" JOIN (");
|
||||||
|
out.push_sql(concat!(
|
||||||
|
"SELECT match_id, player_id, bot_version_id, bot_id ",
|
||||||
|
"FROM match_players ",
|
||||||
|
"JOIN bot_versions ON match_players.bot_version_id = bot_versions.id ",
|
||||||
|
"WHERE bot_id = "
|
||||||
|
));
|
||||||
|
out.push_bind_param::<Integer, _>(&self.bot_id)?;
|
||||||
|
out.push_sql(") main_player ON matches.id = main_player.match_id");
|
||||||
|
|
||||||
|
if let Some(opponent_id) = self.opponent_id.as_ref() {
|
||||||
|
out.push_sql(" JOIN (");
|
||||||
|
out.push_sql(concat!(
|
||||||
|
"SELECT match_id, player_id, bot_version_id, bot_id ",
|
||||||
|
"FROM match_players ",
|
||||||
|
"JOIN bot_versions ON match_players.bot_version_id = bot_versions.id ",
|
||||||
|
"WHERE bot_id = "
|
||||||
|
));
|
||||||
|
out.push_bind_param::<Integer, _>(opponent_id)?;
|
||||||
|
out.push_sql(") other_player ON matches.id = other_player.match_id");
|
||||||
|
}
|
||||||
|
|
||||||
|
out.push_sql(" WHERE matches.state = 'finished' AND matches.is_public = true");
|
||||||
|
if let Some(outcome) = self.outcome.as_ref() {
|
||||||
|
match outcome {
|
||||||
|
BotMatchOutcome::Win => {
|
||||||
|
out.push_sql(" AND matches.winner = main_player.player_id");
|
||||||
|
}
|
||||||
|
BotMatchOutcome::Loss => {
|
||||||
|
out.push_sql(" AND matches.winner <> main_player.player_id");
|
||||||
|
}
|
||||||
|
BotMatchOutcome::Tie => {
|
||||||
|
out.push_sql(" AND matches.winner IS NULL");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(before) = self.before.as_ref() {
|
||||||
|
out.push_sql(" AND matches.created_at < ");
|
||||||
|
out.push_bind_param::<Timestamp, _>(before)?;
|
||||||
|
out.push_sql(" ORDER BY matches.created_at DESC");
|
||||||
|
} else if let Some(after) = self.after.as_ref() {
|
||||||
|
out.push_sql(" AND matches.created_at > ");
|
||||||
|
out.push_bind_param::<Timestamp, _>(after)?;
|
||||||
|
out.push_sql(" ORDER BY matches.created_at ASC");
|
||||||
|
}
|
||||||
|
out.push_sql(" LIMIT ");
|
||||||
|
out.push_bind_param::<BigInt, _>(&self.amount)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Query for ListBotMatches {
|
||||||
|
type SqlType = matches::SqlType;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl QueryId for ListBotMatches {
|
||||||
|
type QueryId = ();
|
||||||
|
|
||||||
|
const HAS_STATIC_QUERY_ID: bool = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RunQueryDsl<PgConnection> for ListBotMatches {}
|
|
@ -14,6 +14,7 @@ use crate::schema::{bot_versions, bots, maps, match_players, matches};
|
||||||
|
|
||||||
use super::bots::{Bot, BotVersion};
|
use super::bots::{Bot, BotVersion};
|
||||||
use super::maps::Map;
|
use super::maps::Map;
|
||||||
|
use super::match_queries::ListBotMatches;
|
||||||
|
|
||||||
#[derive(Insertable)]
|
#[derive(Insertable)]
|
||||||
#[diesel(table_name = matches)]
|
#[diesel(table_name = matches)]
|
||||||
|
@ -173,35 +174,23 @@ pub fn list_public_matches(
|
||||||
|
|
||||||
pub fn list_bot_matches(
|
pub fn list_bot_matches(
|
||||||
bot_id: i32,
|
bot_id: i32,
|
||||||
|
opponent_id: Option<i32>,
|
||||||
outcome: Option<BotMatchOutcome>,
|
outcome: Option<BotMatchOutcome>,
|
||||||
amount: i64,
|
amount: i64,
|
||||||
before: Option<NaiveDateTime>,
|
before: Option<NaiveDateTime>,
|
||||||
after: Option<NaiveDateTime>,
|
after: Option<NaiveDateTime>,
|
||||||
conn: &mut PgConnection,
|
conn: &mut PgConnection,
|
||||||
) -> QueryResult<Vec<FullMatchData>> {
|
) -> QueryResult<Vec<FullMatchData>> {
|
||||||
let mut query = finished_public_matches_query(before, after)
|
let lbm = ListBotMatches {
|
||||||
.inner_join(match_players::table)
|
bot_id,
|
||||||
.inner_join(
|
opponent_id,
|
||||||
bot_versions::table.on(match_players::bot_version_id.eq(bot_versions::id.nullable())),
|
outcome,
|
||||||
)
|
before,
|
||||||
.filter(bot_versions::bot_id.eq(bot_id))
|
after,
|
||||||
.select(matches::all_columns);
|
amount,
|
||||||
|
|
||||||
if let Some(outcome) = outcome {
|
|
||||||
query = match outcome {
|
|
||||||
BotMatchOutcome::Win => {
|
|
||||||
query.filter(matches::winner.eq(match_players::player_id.nullable()))
|
|
||||||
}
|
|
||||||
BotMatchOutcome::Loss => {
|
|
||||||
query.filter(matches::winner.ne(match_players::player_id.nullable()))
|
|
||||||
}
|
|
||||||
BotMatchOutcome::Tie => query.filter(matches::winner.is_null()),
|
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
query = query.limit(amount);
|
let matches = lbm.get_results::<MatchBase>(conn)?;
|
||||||
|
|
||||||
let matches = query.get_results::<MatchBase>(conn)?;
|
|
||||||
fetch_full_match_data(matches, conn)
|
fetch_full_match_data(matches, conn)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
pub mod bots;
|
pub mod bots;
|
||||||
pub mod maps;
|
pub mod maps;
|
||||||
|
pub mod match_queries;
|
||||||
pub mod matches;
|
pub mod matches;
|
||||||
pub mod ratings;
|
pub mod ratings;
|
||||||
pub mod sessions;
|
pub mod sessions;
|
||||||
|
|
|
@ -42,6 +42,7 @@ pub struct ListRecentMatchesParams {
|
||||||
after: Option<NaiveDateTime>,
|
after: Option<NaiveDateTime>,
|
||||||
|
|
||||||
bot: Option<String>,
|
bot: Option<String>,
|
||||||
|
opponent: Option<String>,
|
||||||
outcome: Option<BotMatchOutcome>,
|
outcome: Option<BotMatchOutcome>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,8 +71,18 @@ pub async fn list_recent_matches(
|
||||||
Some(bot_name) => {
|
Some(bot_name) => {
|
||||||
let bot = db::bots::find_bot_by_name(&bot_name, &mut conn)
|
let bot = db::bots::find_bot_by_name(&bot_name, &mut conn)
|
||||||
.map_err(|_| StatusCode::BAD_REQUEST)?;
|
.map_err(|_| StatusCode::BAD_REQUEST)?;
|
||||||
|
|
||||||
|
let opponent_id = if let Some(opponent_name) = params.opponent {
|
||||||
|
let opponent = db::bots::find_bot_by_name(&opponent_name, &mut conn)
|
||||||
|
.map_err(|_| StatusCode::BAD_REQUEST)?;
|
||||||
|
Some(opponent.id)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
matches::list_bot_matches(
|
matches::list_bot_matches(
|
||||||
bot.id,
|
bot.id,
|
||||||
|
opponent_id,
|
||||||
params.outcome,
|
params.outcome,
|
||||||
count,
|
count,
|
||||||
params.before,
|
params.before,
|
||||||
|
@ -82,7 +93,7 @@ pub async fn list_recent_matches(
|
||||||
None => matches::list_public_matches(count, params.before, params.after, &mut conn),
|
None => matches::list_public_matches(count, params.before, params.after, &mut conn),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut matches = matches_result.map_err(|_| StatusCode::BAD_REQUEST)?;
|
let mut matches = matches_result.expect("failed to get matches"); //.map_err(|_| StatusCode::BAD_REQUEST)?;
|
||||||
|
|
||||||
let mut has_next = false;
|
let mut has_next = false;
|
||||||
if matches.len() > requested_count {
|
if matches.len() > requested_count {
|
||||||
|
|
Loading…
Reference in a new issue