Can't even keep track anymore
This commit is contained in:
parent
dccc1e439d
commit
3a3f899ea2
50 changed files with 1144 additions and 560 deletions
97
server-rs/src/routes/places.rs
Normal file
97
server-rs/src/routes/places.rs
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use axum::extract::Query;
|
||||
use axum::http::StatusCode;
|
||||
use axum::response::Json;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tracing::info;
|
||||
|
||||
use crate::state::AppState;
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct PlaceResult {
|
||||
name: String,
|
||||
place_type: String,
|
||||
lat: f32,
|
||||
lon: f32,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct PlacesResponse {
|
||||
places: Vec<PlaceResult>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[allow(clippy::min_ident_chars)]
|
||||
pub struct PlacesParams {
|
||||
q: Option<String>,
|
||||
limit: Option<usize>,
|
||||
}
|
||||
|
||||
pub async fn get_places(
|
||||
state: Arc<AppState>,
|
||||
Query(params): Query<PlacesParams>,
|
||||
) -> Result<Json<PlacesResponse>, (StatusCode, String)> {
|
||||
let query = params
|
||||
.q
|
||||
.filter(|val| !val.is_empty())
|
||||
.ok_or((StatusCode::BAD_REQUEST, "Missing 'q' parameter".to_string()))?;
|
||||
|
||||
let limit = params.limit.unwrap_or(7).min(20);
|
||||
|
||||
let places = tokio::task::spawn_blocking(move || {
|
||||
let t0 = std::time::Instant::now();
|
||||
let query_lower = query.to_lowercase();
|
||||
let pd = &state.place_data;
|
||||
|
||||
// Linear scan — ~50-100k rows, <1ms
|
||||
let mut matches: Vec<(usize, bool, u8, usize)> = pd
|
||||
.name_lower
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(|(idx, name)| {
|
||||
if name.contains(&query_lower) {
|
||||
let is_prefix = name.starts_with(&query_lower);
|
||||
Some((idx, is_prefix, pd.type_rank[idx], pd.name[idx].len()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Sort: prefix first, then by type rank (cities before hamlets), then shorter names first
|
||||
matches.sort_unstable_by(|lhs, rhs| {
|
||||
rhs.1
|
||||
.cmp(&lhs.1)
|
||||
.then(lhs.2.cmp(&rhs.2))
|
||||
.then(lhs.3.cmp(&rhs.3))
|
||||
});
|
||||
|
||||
matches.truncate(limit);
|
||||
|
||||
let results: Vec<PlaceResult> = matches
|
||||
.iter()
|
||||
.map(|&(idx, ..)| PlaceResult {
|
||||
name: pd.name[idx].clone(),
|
||||
place_type: pd.place_type.get(idx).to_string(),
|
||||
lat: pd.lat[idx],
|
||||
lon: pd.lon[idx],
|
||||
})
|
||||
.collect();
|
||||
|
||||
let elapsed = t0.elapsed();
|
||||
info!(
|
||||
query = query.as_str(),
|
||||
results = results.len(),
|
||||
scanned = pd.name_lower.len(),
|
||||
ms = format_args!("{:.1}", elapsed.as_secs_f64() * 1000.0),
|
||||
"GET /api/places"
|
||||
);
|
||||
|
||||
results
|
||||
})
|
||||
.await
|
||||
.map_err(|error| (StatusCode::INTERNAL_SERVER_ERROR, error.to_string()))?;
|
||||
|
||||
Ok(Json(PlacesResponse { places }))
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue