diff --git a/blog-server-api/src/endpoints/authors/handler.rs b/blog-server-api/src/endpoints/authors/handler.rs index beb269e..392005e 100644 --- a/blog-server-api/src/endpoints/authors/handler.rs +++ b/blog-server-api/src/endpoints/authors/handler.rs @@ -9,6 +9,7 @@ use screw_api::response::ApiResponse; use std::sync::Arc; async fn handler( + query: Option, offset: Option, limit: Option, author_service: Arc>, @@ -16,10 +17,17 @@ async fn handler( let offset = offset.unwrap_or(0).max(0); let limit = limit.unwrap_or(50).max(0).min(50); - let (authors_result, total_result) = tokio::join!( - author_service.authors(&offset, &limit), - author_service.authors_count(), - ); + let (authors_result, total_result) = if let Some(query) = query { + tokio::join!( + author_service.authors_by_query(&query, &offset, &limit), + author_service.authors_count_by_query(&query), + ) + } else { + tokio::join!( + author_service.authors(&offset, &limit), + author_service.authors_count(), + ) + }; let authors = authors_result .map_err(|e| DatabaseError { @@ -49,6 +57,7 @@ where { ApiResponse::from( handler( + request.content.query, request.content.offset, request.content.limit, request.content.author_service, diff --git a/blog-server-api/src/endpoints/authors/request_content.rs b/blog-server-api/src/endpoints/authors/request_content.rs index 4d5f867..a9ec6a3 100644 --- a/blog-server-api/src/endpoints/authors/request_content.rs +++ b/blog-server-api/src/endpoints/authors/request_content.rs @@ -4,6 +4,7 @@ use screw_api::request::{ApiRequestContent, ApiRequestOriginContent}; use std::sync::Arc; pub struct AuthorsRequestContent { + pub(super) query: Option, pub(super) offset: Option, pub(super) limit: Option, pub(super) author_service: Arc>, @@ -17,6 +18,7 @@ where fn create(origin_content: ApiRequestOriginContent) -> Self { Self { + query: origin_content.path.get("query").map(|n| n.to_owned()), offset: origin_content .query .get("offset") diff --git a/blog-server-api/src/endpoints/posts/handler.rs b/blog-server-api/src/endpoints/posts/handler.rs index aca5cee..43cc57b 100644 --- a/blog-server-api/src/endpoints/posts/handler.rs +++ b/blog-server-api/src/endpoints/posts/handler.rs @@ -7,9 +7,9 @@ use blog_server_services::traits::post_service::PostService; use screw_api::request::ApiRequest; use screw_api::response::ApiResponse; use std::sync::Arc; -use tokio::join; async fn handler( + query: Option, offset: Option, limit: Option, post_service: Arc>, @@ -17,10 +17,17 @@ async fn handler( let offset = offset.unwrap_or(0).max(0); let limit = limit.unwrap_or(50).max(0).min(50); - let (posts_result, total_result) = join!( - post_service.posts(&offset, &limit), - post_service.posts_count(), - ); + let (posts_result, total_result) = if let Some(query) = query { + tokio::join!( + post_service.posts_by_query(&query, &offset, &limit), + post_service.posts_count_by_query(&query), + ) + } else { + tokio::join!( + post_service.posts(&offset, &limit), + post_service.posts_count(), + ) + }; let posts = posts_result .map_err(|e| DatabaseError { @@ -50,6 +57,7 @@ where { ApiResponse::from( handler( + request.content.query, request.content.offset, request.content.limit, request.content.post_service, diff --git a/blog-server-api/src/endpoints/posts/request_content.rs b/blog-server-api/src/endpoints/posts/request_content.rs index 816e0bf..de11b0c 100644 --- a/blog-server-api/src/endpoints/posts/request_content.rs +++ b/blog-server-api/src/endpoints/posts/request_content.rs @@ -4,6 +4,7 @@ use screw_api::request::{ApiRequestContent, ApiRequestOriginContent}; use std::sync::Arc; pub struct PostsRequestContent { + pub(super) query: Option, pub(super) offset: Option, pub(super) limit: Option, pub(super) post_service: Arc>, @@ -17,6 +18,7 @@ where fn create(origin_content: ApiRequestOriginContent) -> Self { Self { + query: origin_content.path.get("query").map(|n| n.to_owned()), offset: origin_content .query .get("offset") diff --git a/blog-server-api/src/main.rs b/blog-server-api/src/main.rs index 274cdbe..d58000b 100644 --- a/blog-server-api/src/main.rs +++ b/blog-server-api/src/main.rs @@ -26,8 +26,36 @@ async fn main() -> screw_components::dyn_result::DResult<()> { Ok(()) } +#[derive(Debug)] +struct DbgRbatisIntercept; + +impl rbatis::intercept::Intercept for DbgRbatisIntercept { + fn before( + &self, + _task_id: i64, + _rb: &dyn rbatis::executor::Executor, + sql: &mut String, + args: &mut Vec, + ) -> Result<(), rbatis::Error> { + dbg!(sql); + dbg!(args); + Ok(()) + } +} + pub async fn init_db() -> rbatis::RBatis { - let rb = rbatis::RBatis::new(); + let opt = rbatis::RBatisOption { + intercepts: { + let intercepts: rbatis::dark_std::sync::SyncVec< + std::sync::Arc, + > = rbatis::dark_std::sync::SyncVec::new(); + if cfg!(debug_assertions) { + intercepts.push(std::sync::Arc::new(DbgRbatisIntercept)); + } + intercepts + }, + }; + let rb = rbatis::RBatis::new_with_opt(opt); rb.init( rbdc_pg::driver::PgDriver {}, std::env::var("PG_URL") diff --git a/blog-server-api/src/router.rs b/blog-server-api/src/router.rs index 97ba42c..02128a8 100644 --- a/blog-server-api/src/router.rs +++ b/blog-server-api/src/router.rs @@ -80,6 +80,18 @@ pub fn make_router( .and_path("/posts") .and_handler(posts::http_handler), ) + .scoped("/search", |r| { + r.route( + route::first::Route::with_method(&hyper::Method::GET) + .and_path("/posts/{query:[^/]*}") + .and_handler(posts::http_handler), + ) + .route( + route::first::Route::with_method(&hyper::Method::GET) + .and_path("/authors/{query:[^/]*}") + .and_handler(authors::http_handler), + ) + }) .route( route::first::Route::with_method(&hyper::Method::GET) .and_path("/comments/{post_slug:[^/]*}") diff --git a/blog-server-services/src/impls/rbatis_author_service.rs b/blog-server-services/src/impls/rbatis_author_service.rs index 005347c..ac537c1 100644 --- a/blog-server-services/src/impls/rbatis_author_service.rs +++ b/blog-server-services/src/impls/rbatis_author_service.rs @@ -7,9 +7,14 @@ pub fn create_rbatis_author_service(rb: RBatis) -> Box { } impl_insert!(BaseAuthor {}, "author"); -impl_select!(Author {select_by_id(id: &i64) -> Option => "`WHERE id = #{id} LIMIT 1`"}); -impl_select!(Author {select_by_slug(slug: &String) -> Option => "`WHERE slug = #{slug} LIMIT 1`"}); -impl_select!(Author {select_all_with_offset_and_limit(offset: &i64, limit: &i64) => "`LIMIT #{limit} OFFSET #{offset}`"}); +impl_select!(Author {select_by_id(id: &i64) -> Option => + "`WHERE id = #{id} LIMIT 1`"}); +impl_select!(Author {select_by_slug(slug: &String) -> Option => + "`WHERE slug = #{slug} LIMIT 1`"}); +impl_select!(Author {select_all_with_offset_and_limit(offset: &i64, limit: &i64) => + "`LIMIT #{limit} OFFSET #{offset}`"}); +impl_select!(Author {select_all_by_query_with_offset_and_limit(query: &String, offset: &i64, limit: &i64) => + "`WHERE author.slug ILIKE '%' || #{query} || '%' OR author.first_name ILIKE '%' || #{query} || '%' OR author.middle_name ILIKE '%' || #{query} || '%' OR author.last_name ILIKE '%' || #{query} || '%' LIMIT #{limit} OFFSET #{offset}`"}); impl Author { #[py_sql( @@ -21,6 +26,16 @@ impl Author { async fn count(rb: &RBatis) -> rbatis::Result { impled!() } + #[py_sql( + " + SELECT COUNT(1) \ + FROM author \ + WHERE author.slug ILIKE '%' || #{query} || '%' OR author.first_name ILIKE '%' || #{query} || '%' OR author.middle_name ILIKE '%' || #{query} || '%' OR author.last_name ILIKE '%' || #{query} || '%' \ + " + )] + async fn count_by_query(rb: &RBatis, query: &String) -> rbatis::Result { + impled!() + } } struct RbatisAuthorService { @@ -29,6 +44,23 @@ struct RbatisAuthorService { #[async_trait] impl AuthorService for RbatisAuthorService { + async fn authors_count_by_query(&self, query: &String) -> DResult { + Ok(Author::count_by_query(&self.rb, query).await?) + } + async fn authors_by_query( + &self, + query: &String, + offset: &i64, + limit: &i64, + ) -> DResult> { + Ok(Author::select_all_by_query_with_offset_and_limit( + &mut self.rb.clone(), + query, + offset, + limit, + ) + .await?) + } async fn authors_count(&self) -> DResult { Ok(Author::count(&self.rb).await?) } diff --git a/blog-server-services/src/impls/rbatis_comment_service.rs b/blog-server-services/src/impls/rbatis_comment_service.rs index 7104be6..762ac02 100644 --- a/blog-server-services/src/impls/rbatis_comment_service.rs +++ b/blog-server-services/src/impls/rbatis_comment_service.rs @@ -33,7 +33,7 @@ impl Comment { OFFSET #{offset} \ " )] - async fn select_all_with_post_id_and_limit_and_offset( + async fn select_all_by_post_id_with_limit_and_offset( rb: &RBatis, post_id: &i64, limit: &i64, @@ -59,7 +59,7 @@ impl CommentService for RbatisCommentService { limit: &i64, ) -> DResult> { Ok( - Comment::select_all_with_post_id_and_limit_and_offset(&self.rb, post_id, limit, offset) + Comment::select_all_by_post_id_with_limit_and_offset(&self.rb, post_id, limit, offset) .await?, ) } diff --git a/blog-server-services/src/impls/rbatis_post_service.rs b/blog-server-services/src/impls/rbatis_post_service.rs index 8c79b63..1a16f00 100644 --- a/blog-server-services/src/impls/rbatis_post_service.rs +++ b/blog-server-services/src/impls/rbatis_post_service.rs @@ -18,6 +18,16 @@ impl Post { async fn count(rb: &RBatis) -> rbatis::Result { impled!() } + #[py_sql( + " + SELECT COUNT(1) \ + FROM post \ + WHERE post.title ILIKE '%' || #{query} || '%' OR post.summary ILIKE '%' || #{query} || '%' OR post.content ILIKE '%' || #{query} || '%' \ + " + )] + async fn count_by_query(rb: &RBatis, query: &String) -> rbatis::Result { + impled!() + } #[py_sql( " SELECT \ @@ -82,6 +92,32 @@ impl Post { ) -> rbatis::Result> { impled!() } + #[py_sql( + " + SELECT \ + post.*, \ + author.slug AS author_slug, \ + author.first_name AS author_first_name, \ + author.last_name AS author_last_name, \ + string_agg(concat_ws(',', tag.slug, tag.title), ';') as tags \ + FROM post \ + JOIN author ON post.author_id = author.id \ + LEFT JOIN post_tag ON post_tag.post_id = post.id \ + LEFT JOIN tag ON tag.id = post_tag.tag_id \ + WHERE post.title ILIKE '%' || #{query} || '%' OR post.summary ILIKE '%' || #{query} || '%' OR post.content ILIKE '%' || #{query} || '%' \ + GROUP BY post.id, author.slug, author.first_name, author.last_name \ + LIMIT #{limit} \ + OFFSET #{offset} \ + " + )] + async fn select_all_by_query_with_limit_and_offset( + rb: &RBatis, + query: &String, + limit: &i64, + offset: &i64, + ) -> rbatis::Result> { + impled!() + } } struct RbatisPostService { @@ -90,6 +126,17 @@ struct RbatisPostService { #[async_trait] impl PostService for RbatisPostService { + async fn posts_count_by_query(&self, query: &String) -> DResult { + Ok(Post::count_by_query(&self.rb, query).await?) + } + async fn posts_by_query( + &self, + query: &String, + offset: &i64, + limit: &i64, + ) -> DResult> { + Ok(Post::select_all_by_query_with_limit_and_offset(&self.rb, query, limit, offset).await?) + } async fn posts_count(&self) -> DResult { Ok(Post::count(&self.rb).await?) } diff --git a/blog-server-services/src/traits/author_service.rs b/blog-server-services/src/traits/author_service.rs index b80e8af..2acf0f4 100644 --- a/blog-server-services/src/traits/author_service.rs +++ b/blog-server-services/src/traits/author_service.rs @@ -25,6 +25,13 @@ pub struct Author { #[async_trait] pub trait AuthorService: Send + Sync { + async fn authors_count_by_query(&self, query: &String) -> DResult; + async fn authors_by_query( + &self, + query: &String, + offset: &i64, + limit: &i64, + ) -> DResult>; async fn authors_count(&self) -> DResult; async fn authors(&self, offset: &i64, limit: &i64) -> DResult>; async fn author_by_id(&self, id: &i64) -> DResult>; diff --git a/blog-server-services/src/traits/post_service.rs b/blog-server-services/src/traits/post_service.rs index 033a151..9fd4e03 100644 --- a/blog-server-services/src/traits/post_service.rs +++ b/blog-server-services/src/traits/post_service.rs @@ -27,6 +27,9 @@ pub struct Post { #[async_trait] pub trait PostService: Send + Sync { + async fn posts_count_by_query(&self, query: &String) -> DResult; + async fn posts_by_query(&self, query: &String, offset: &i64, limit: &i64) + -> DResult>; async fn posts_count(&self) -> DResult; async fn posts(&self, offset: &i64, limit: &i64) -> DResult>; async fn post_by_id(&self, id: &i64) -> DResult>;