lmao
This commit is contained in:
parent
03445188ea
commit
524580eb25
102 changed files with 36625 additions and 1295 deletions
|
|
@ -6,11 +6,13 @@ use axum::response::Json;
|
|||
use serde::{Deserialize, Serialize};
|
||||
use tracing::info;
|
||||
|
||||
use crate::data::slugify;
|
||||
use crate::state::AppState;
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct PlaceResult {
|
||||
name: String,
|
||||
slug: String,
|
||||
place_type: String,
|
||||
lat: f32,
|
||||
lon: f32,
|
||||
|
|
@ -28,6 +30,8 @@ pub struct PlacesResponse {
|
|||
pub struct PlacesParams {
|
||||
q: String,
|
||||
limit: Option<usize>,
|
||||
/// If set, only return places that have travel time data for this mode.
|
||||
mode: Option<String>,
|
||||
}
|
||||
|
||||
pub async fn get_places(
|
||||
|
|
@ -41,33 +45,44 @@ pub async fn get_places(
|
|||
};
|
||||
|
||||
let limit = params.limit.unwrap_or(7).min(20);
|
||||
let mode_filter = params.mode;
|
||||
|
||||
let places = tokio::task::spawn_blocking(move || {
|
||||
let t0 = std::time::Instant::now();
|
||||
let query_lower = query.to_lowercase();
|
||||
let pd = &state.place_data;
|
||||
let tt_store = &state.travel_time_store;
|
||||
|
||||
// Linear scan — ~50-100k rows, <1ms
|
||||
// Tuple: (row_idx, is_exact, is_prefix, type_rank, population, name_len)
|
||||
let mut matches: Vec<(usize, bool, bool, u8, u32, usize)> = pd
|
||||
// Tuple: (row_idx, is_exact, is_prefix, type_rank, population, name_len, slug)
|
||||
let mut matches: Vec<(usize, bool, bool, u8, u32, usize, String)> = pd
|
||||
.name_lower
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(|(idx, name)| {
|
||||
if name.contains(&query_lower) {
|
||||
let is_exact = name.len() == query_lower.len();
|
||||
let is_prefix = name.starts_with(&query_lower);
|
||||
Some((
|
||||
idx,
|
||||
is_exact,
|
||||
is_prefix,
|
||||
pd.type_rank[idx],
|
||||
pd.population[idx],
|
||||
pd.name[idx].len(),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
if !name.contains(&query_lower) {
|
||||
return None;
|
||||
}
|
||||
let slug = slugify(&pd.name[idx]);
|
||||
|
||||
// If mode filter is set, only include places with travel data
|
||||
if let Some(ref mode) = mode_filter {
|
||||
if !tt_store.has_destination(mode, &slug) {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
let is_exact = name.len() == query_lower.len();
|
||||
let is_prefix = name.starts_with(&query_lower);
|
||||
Some((
|
||||
idx,
|
||||
is_exact,
|
||||
is_prefix,
|
||||
pd.type_rank[idx],
|
||||
pd.population[idx],
|
||||
pd.name[idx].len(),
|
||||
slug,
|
||||
))
|
||||
})
|
||||
.collect();
|
||||
|
||||
|
|
@ -85,12 +100,13 @@ pub async fn get_places(
|
|||
|
||||
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],
|
||||
city: pd.city[idx].clone(),
|
||||
.map(|(idx, .., slug)| PlaceResult {
|
||||
name: pd.name[*idx].clone(),
|
||||
slug: slug.clone(),
|
||||
place_type: pd.place_type.get(*idx).to_string(),
|
||||
lat: pd.lat[*idx],
|
||||
lon: pd.lon[*idx],
|
||||
city: pd.city[*idx].clone(),
|
||||
})
|
||||
.collect();
|
||||
|
||||
|
|
@ -99,6 +115,7 @@ pub async fn get_places(
|
|||
query = query.as_str(),
|
||||
results = results.len(),
|
||||
scanned = pd.name_lower.len(),
|
||||
mode = mode_filter.as_deref().unwrap_or("-"),
|
||||
ms = format_args!("{:.1}", elapsed.as_secs_f64() * 1000.0),
|
||||
"GET /api/places"
|
||||
);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue