2021-11-07 21:23:18 +00:00
|
|
|
use crate::commands::{Command, RequestType};
|
|
|
|
use crate::context::Context;
|
|
|
|
use crate::Result;
|
2021-11-14 23:28:55 +00:00
|
|
|
use geoffrey_models::models::item::ItemListing;
|
2021-11-07 21:23:18 +00:00
|
|
|
use geoffrey_models::models::locations::{LocationDataDb, LocationDb};
|
2021-11-14 23:28:55 +00:00
|
|
|
use geoffrey_models::models::parameters::selling_params::{ItemSort, Order, SellingParams};
|
2021-11-07 21:23:18 +00:00
|
|
|
use geoffrey_models::models::player::Player;
|
|
|
|
use geoffrey_models::models::response::selling_listing::SellingListing;
|
|
|
|
use geoffrey_models::models::CommandLevel;
|
|
|
|
use std::sync::Arc;
|
|
|
|
|
|
|
|
pub struct Selling {}
|
|
|
|
|
|
|
|
impl Command for Selling {
|
|
|
|
type Req = SellingParams;
|
|
|
|
type Resp = Vec<SellingListing>;
|
|
|
|
|
|
|
|
fn command_name() -> String {
|
|
|
|
"selling".to_string()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn request_type() -> RequestType {
|
|
|
|
RequestType::GET
|
|
|
|
}
|
|
|
|
|
|
|
|
fn command_level() -> CommandLevel {
|
|
|
|
CommandLevel::ALL
|
|
|
|
}
|
|
|
|
|
|
|
|
fn run_command(ctx: Arc<Context>, req: Self::Req, _: Option<Player>) -> Result<Self::Resp> {
|
2021-11-14 23:28:55 +00:00
|
|
|
let mut listings: Vec<SellingListing> = ctx
|
2021-11-07 21:23:18 +00:00
|
|
|
.db
|
|
|
|
.filter(|_, loc: &LocationDb| {
|
|
|
|
if let LocationDataDb::Shop(shop_data) = loc.loc_data.clone() {
|
|
|
|
shop_data.item_listings.iter().any(|item| {
|
|
|
|
item.item
|
|
|
|
.name
|
|
|
|
.to_lowercase()
|
|
|
|
.contains(&req.query.to_lowercase())
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
false
|
|
|
|
}
|
|
|
|
})?
|
|
|
|
.filter_map(|shop: LocationDb| {
|
|
|
|
if let LocationDataDb::Shop(shop_data) = shop.clone().loc_data {
|
|
|
|
let listings: Vec<SellingListing> = shop_data
|
|
|
|
.item_listings
|
|
|
|
.iter()
|
|
|
|
.filter_map(|item| {
|
|
|
|
if item.item.name.to_lowercase().contains(&req.query) {
|
|
|
|
Some(SellingListing {
|
|
|
|
listing: item.clone(),
|
|
|
|
shop_name: shop.name.clone(),
|
|
|
|
shop_loc: shop.position,
|
2021-12-08 03:33:25 +00:00
|
|
|
portal: shop.portal.clone(),
|
2021-11-07 21:23:18 +00:00
|
|
|
})
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
Some(listings)
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.flatten()
|
|
|
|
.collect();
|
|
|
|
|
2021-11-14 23:28:55 +00:00
|
|
|
let sort = req.sort.unwrap_or(ItemSort::Restock);
|
|
|
|
|
|
|
|
match sort {
|
|
|
|
ItemSort::Price => {
|
|
|
|
listings.sort_by(|a, b| ItemListing::order_by_price(&a.listing, &b.listing))
|
|
|
|
}
|
|
|
|
ItemSort::Restock => {
|
|
|
|
listings.sort_by(|a, b| ItemListing::order_by_restock(&a.listing, &b.listing))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let ordering = if let Some(order) = req.order {
|
|
|
|
order
|
|
|
|
} else {
|
|
|
|
match sort {
|
|
|
|
ItemSort::Price => Order::Low,
|
|
|
|
ItemSort::Restock => Order::High,
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
if ordering == Order::High {
|
|
|
|
listings.reverse();
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(listings)
|
2021-11-07 21:23:18 +00:00
|
|
|
}
|
|
|
|
}
|
2021-11-14 22:38:32 +00:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod test {
|
|
|
|
use crate::commands::selling::Selling;
|
|
|
|
use crate::commands::Command;
|
|
|
|
use crate::config::GeoffreyAPIConfig;
|
|
|
|
use crate::context::Context;
|
|
|
|
use geoffrey_models::models::item::ItemListing;
|
|
|
|
use geoffrey_models::models::locations::shop::Shop;
|
|
|
|
use geoffrey_models::models::locations::{LocationDataDb, LocationDb};
|
|
|
|
use geoffrey_models::models::parameters::selling_params::SellingParams;
|
|
|
|
use geoffrey_models::models::Position;
|
|
|
|
use std::path::PathBuf;
|
|
|
|
use std::time::Instant;
|
|
|
|
|
|
|
|
fn test_selling_lookup_speed(iter: i32, shop_count: i32, items_for_sale: i32) -> f32 {
|
|
|
|
let config = GeoffreyAPIConfig {
|
|
|
|
db_path: PathBuf::from("test_db/"),
|
|
|
|
host: "".to_string(),
|
|
|
|
};
|
|
|
|
|
|
|
|
let ctx = Context::new(config.clone()).unwrap();
|
|
|
|
|
|
|
|
let mut shop_data = Shop::default();
|
|
|
|
|
|
|
|
for _ in 0..items_for_sale {
|
|
|
|
shop_data
|
|
|
|
.item_listings
|
|
|
|
.insert(ItemListing::new("sed", 5, 5));
|
|
|
|
}
|
|
|
|
|
|
|
|
for i in 0..shop_count {
|
|
|
|
let shop = LocationDb::new(
|
|
|
|
format!("Test Shop {} {}", iter, i).as_str(),
|
|
|
|
Position::default(),
|
|
|
|
0,
|
|
|
|
None,
|
|
|
|
LocationDataDb::Shop(shop_data.clone()),
|
|
|
|
);
|
|
|
|
ctx.db.insert(shop).unwrap();
|
|
|
|
}
|
|
|
|
|
|
|
|
let start = Instant::now();
|
|
|
|
|
|
|
|
Selling::run_command(
|
|
|
|
ctx,
|
|
|
|
SellingParams {
|
|
|
|
token: "".to_string(),
|
|
|
|
query: "sed".to_string(),
|
|
|
|
sort: None,
|
|
|
|
order: None,
|
|
|
|
},
|
|
|
|
None,
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let query_dur = start.elapsed().as_secs_f32();
|
|
|
|
|
|
|
|
return query_dur;
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn stress_test() {
|
|
|
|
std::fs::remove_dir_all(PathBuf::from("test_db")).ok();
|
|
|
|
println!("Shop Count,Item Count,Query Time,Query Time / Shop");
|
2021-11-15 00:04:03 +00:00
|
|
|
for shop_count in (100..=500).step_by(100) {
|
2021-11-14 22:38:32 +00:00
|
|
|
let query_dur = test_selling_lookup_speed(shop_count, 100, 10);
|
|
|
|
|
|
|
|
println!(
|
|
|
|
"{},{},{},{}",
|
|
|
|
shop_count,
|
|
|
|
10,
|
|
|
|
query_dur,
|
|
|
|
query_dur / (shop_count as f32)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
std::fs::remove_dir_all(PathBuf::from("test_db")).ok();
|
|
|
|
}
|
|
|
|
}
|