From 76db90618c0d6be2bf862d0c22810b35a92d7ccf Mon Sep 17 00:00:00 2001 From: Nulo Date: Sun, 4 Aug 2024 15:30:59 -0300 Subject: [PATCH] search --- rust/src/api/main.rs | 46 +++++++++++++++++++++++++ sitio/src/routes/search/+page.server.ts | 41 ++++++++++++---------- 2 files changed, 68 insertions(+), 19 deletions(-) diff --git a/rust/src/api/main.rs b/rust/src/api/main.rs index a6e1eb5..3d83189 100644 --- a/rust/src/api/main.rs +++ b/rust/src/api/main.rs @@ -213,6 +213,51 @@ order by fetched_at Json(precios) } +async fn search(State(pool): State, Path(query): Path) -> impl IntoResponse { + let sql_query = query + .clone() + .replace("\"", "\"\"") + .split(" ") + .map(|x| format!("\"{}\"", x)) + .join(" "); + + #[derive(Serialize)] + struct Result { + ean: String, + name: String, + image_url: String, + } + + let results = sqlx::query!( + "with search_results as ( + select f.ean from precios_fts f + where f.name match ? and f.ean != '' + group by f.ean + limit 100 + ) + select p.id, p.ean, p.name, p.image_url from search_results as s + join precios as p + on p.ean = s.ean + where p.fetched_at = ( + SELECT MAX(fetched_at) + FROM precios as pf + WHERE pf.ean = s.ean and pf.name is not null + );", + sql_query + ) + .fetch_all(&pool) + .await + .unwrap() + .into_iter() + .map(|r| Result { + ean: r.ean, + image_url: r.image_url.unwrap(), + name: r.name.unwrap(), + }) + .collect_vec(); + + Json(results) +} async fn get_info(State(pool): State) -> impl IntoResponse { #[derive(Serialize)] @@ -267,6 +312,7 @@ async fn main() { .route("/api/0/best-selling-products", get(get_best_selling)) .route("/api/0/ean/:ean/history", get(get_product_history)) .route("/api/0/info", get(get_info)) + .route("/api/0/search/:query", get(search)) .with_state(pool); let listener = tokio::net::TcpListener::bind("0.0.0.0:8000").await.unwrap(); diff --git a/sitio/src/routes/search/+page.server.ts b/sitio/src/routes/search/+page.server.ts index 8139fe1..c632796 100644 --- a/sitio/src/routes/search/+page.server.ts +++ b/sitio/src/routes/search/+page.server.ts @@ -1,26 +1,29 @@ -import { sql } from "drizzle-orm"; +import { z } from "zod"; import type { PageServerLoad } from "./$types"; -import { getDb } from "$lib/server/db"; +import { API_HOST } from "$lib"; +import ky from "ky"; + +const zProductResult = z.object({ + ean: z.string(), + name: z.string(), + image_url: z.string(), +}); + +async function search(query: string) { + return z + .array(zProductResult) + .parse( + await ky + .get(`${API_HOST}/api/0/search/${encodeURIComponent(query)}`) + .json(), + ); +} export const load: PageServerLoad = async ({ url }) => { - const db = await getDb(); const query = url.searchParams.get("q"); - let results: null | { ean: string; name: string; imageUrl: string }[] = null; - if (query) { - const sQuery = query - .replaceAll(`"`, `""`) - .split(" ") - .map((s) => `"${s}"`) - .join(" "); - console.debug(sQuery); - const sqlQuery = sql`select p.ean, p.name, p.image_url as imageUrl from precios_fts f - join precios p on p.ean = f.ean - where f.name match ${sQuery} - group by p.ean - having max(p.fetched_at) - order by p.in_stock desc;`; - results = db.all(sqlQuery); - } + let results: null | { ean: string; name: string; image_url: string }[] = query + ? await search(query) + : null; return { query, results }; };