use axum::{ Json, extract::State, http::StatusCode, response::{IntoResponse, Response}, }; use serde::{Deserialize, Serialize}; use std::{sync::Arc, time::Instant}; use tracing::debug; use utoipa::ToSchema; use crate::Datasets; #[derive(Serialize, Deserialize, ToSchema, Debug)] pub struct LookupRequest { pub query: String, #[serde(default)] pub limit: Option, } #[derive(Debug, Serialize, Deserialize, ToSchema)] pub struct LookupResponse { pub results: Vec, pub total: u64, } #[derive(Debug, Serialize, Deserialize, ToSchema)] pub struct LookupResult { pub score: f32, pub source: String, pub key: String, } /// Search for an item in this dataset #[utoipa::path( post, path = "/lookup", responses( (status = 200, description = "Search results", body = Vec), (status = 400, description = "Invalid request"), (status = 401, description = "Unauthorized"), (status = 404, description = "URL not found"), (status = 500, description = "Internal server error"), ) )] pub async fn lookup( State(state): State>, Json(body): Json, ) -> Response { let start = Instant::now(); let limit = body.limit.unwrap_or(128).min(1024); debug!(message = "Serving /lookup", query = body.query, limit); let results: Vec = match state.fts_lookup(&body.query, limit) { Ok(x) => x .into_iter() .map(|x| LookupResult { key: x.key, score: x.score, source: x.source.into(), }) .collect(), Err(error) => { return (StatusCode::INTERNAL_SERVER_ERROR, format!("{error:?}")).into_response(); } }; let total: u64 = state.sources.iter().map(|x| x.1.len() as u64).sum(); debug!( message = "Served /lookup", query = body.query, limit = body.limit.unwrap_or(10), time_ms = start.elapsed().as_millis() ); return (StatusCode::OK, Json(LookupResponse { results, total })).into_response(); }