diff --git a/crates/presentation/Cargo.toml b/crates/presentation/Cargo.toml index 8290c44..fea57d7 100644 --- a/crates/presentation/Cargo.toml +++ b/crates/presentation/Cargo.toml @@ -12,6 +12,7 @@ domain = { workspace = true } application = { workspace = true } api-types = { workspace = true } postgres = { workspace = true } +postgres-search = { workspace = true } auth = { workspace = true } axum = { workspace = true } sqlx = { workspace = true } diff --git a/crates/presentation/src/handlers/feed.rs b/crates/presentation/src/handlers/feed.rs index afe29ac..1e4bae9 100644 --- a/crates/presentation/src/handlers/feed.rs +++ b/crates/presentation/src/handlers/feed.rs @@ -1,6 +1,6 @@ use axum::{extract::{Path, Query, State}, Json}; use api_types::requests::{PaginationQuery, SearchQuery}; -use application::use_cases::feed::{get_home_feed, get_public_feed, get_followers, get_following, search}; +use application::use_cases::feed::{get_home_feed, get_public_feed, get_followers, get_following}; use domain::models::feed::PageParams; use crate::{errors::ApiError, extractors::{AuthUser, OptionalAuthUser}, handlers::auth::to_user_response, state::AppState}; use application::use_cases::profile::get_user_by_username; @@ -17,10 +17,36 @@ pub async fn public_feed(State(s): State, OptionalAuthUser(viewer): Op Ok(Json(serde_json::json!({ "items": result.items.iter().map(|e| e.thought.id.as_uuid()).collect::>(), "total": result.total, "page": result.page }))) } -pub async fn search_handler(State(s): State, OptionalAuthUser(viewer): OptionalAuthUser, Query(q): Query) -> Result, ApiError> { +pub async fn search_handler( + State(s): State, + OptionalAuthUser(viewer): OptionalAuthUser, + Query(q): Query, +) -> Result, ApiError> { let page = PageParams { page: q.page.unwrap_or(1), per_page: q.per_page.unwrap_or(20) }; - let result = search(&*s.feed, &q.q, page, viewer.as_ref()).await?; - Ok(Json(serde_json::json!({ "items": result.items.iter().map(|e| e.thought.id.as_uuid()).collect::>(), "total": result.total }))) + let query = q.q.trim().to_string(); + + let (thoughts_result, users_result) = tokio::join!( + s.search.search_thoughts(&query, &page, viewer.as_ref()), + s.search.search_users(&query, &page), + ); + + let thoughts = thoughts_result?.items.into_iter().map(|e| serde_json::json!({ + "id": e.thought.id.as_uuid(), + "content": e.thought.content.as_str(), + "author": to_user_response(&e.author), + "like_count": e.like_count, + "boost_count": e.boost_count, + "reply_count": e.reply_count, + "created_at": e.thought.created_at, + })).collect::>(); + + let users = users_result?.items.into_iter().map(|u| to_user_response(&u)).collect::>(); + + Ok(Json(serde_json::json!({ + "query": query, + "thoughts": thoughts, + "users": users, + }))) } pub async fn get_following_handler(State(s): State, Path(username): Path, Query(q): Query) -> Result, ApiError> { diff --git a/crates/presentation/src/lib.rs b/crates/presentation/src/lib.rs index e757005..aa90e69 100644 --- a/crates/presentation/src/lib.rs +++ b/crates/presentation/src/lib.rs @@ -7,6 +7,7 @@ pub mod state; use std::sync::Arc; use sqlx::PgPool; use state::AppState; +use postgres_search::PgSearchRepository; use async_trait::async_trait; use domain::{errors::DomainError, events::DomainEvent, ports::EventPublisher}; @@ -34,6 +35,7 @@ pub fn build_state(pool: PgPool, jwt_secret: String) -> AppState { notifications: Arc::new(postgres::notification::PgNotificationRepository::new(pool.clone())), remote_actors: Arc::new(postgres::remote_actor::PgRemoteActorRepository::new(pool.clone())), feed: Arc::new(postgres::feed::PgFeedRepository::new(pool.clone())), + search: Arc::new(PgSearchRepository::new(pool.clone())), auth: Arc::new(auth::JwtAuthService::new(jwt_secret, 86400 * 30)), hasher: Arc::new(auth::Argon2PasswordHasher), events: Arc::new(NoOpEventPublisher), diff --git a/crates/presentation/src/state.rs b/crates/presentation/src/state.rs index c615716..c582001 100644 --- a/crates/presentation/src/state.rs +++ b/crates/presentation/src/state.rs @@ -15,6 +15,7 @@ pub struct AppState { pub notifications: Arc, pub remote_actors: Arc, pub feed: Arc, + pub search: Arc, pub auth: Arc, pub hasher: Arc, pub events: Arc,