From d18f2c9eb1ca4b121886fe0f2def426bf5ce1ad6 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Sun, 8 Dec 2019 12:39:54 -0800 Subject: [PATCH] Use builder pattern for views - Fixes #360 --- server/src/api/community.rs | 10 +- server/src/api/post.rs | 16 +- server/src/api/site.rs | 80 +++++---- server/src/api/user.rs | 92 ++++------ server/src/db/comment_view.rs | 261 ++++++++++++++++++++++------- server/src/db/community_view.rs | 184 ++++++++++++++------ server/src/db/user_mention_view.rs | 89 ++++++++-- server/src/db/user_view.rs | 83 +++++++-- server/src/feeds.rs | 12 +- 9 files changed, 562 insertions(+), 265 deletions(-) diff --git a/server/src/api/community.rs b/server/src/api/community.rs index 69113d522..5bd600077 100644 --- a/server/src/api/community.rs +++ b/server/src/api/community.rs @@ -349,9 +349,13 @@ impl Perform for Oper { let sort = SortType::from_str(&data.sort)?; - let communities: Vec = CommunityView::list( - &conn, &sort, user_id, show_nsfw, None, data.page, data.limit, - )?; + let communities = CommunityQueryBuilder::create(&conn) + .sort(&sort) + .from_user_id_optional(user_id) + .show_nsfw(show_nsfw) + .page_optional(data.page) + .limit_optional(data.limit) + .list()?; // Return the jwt Ok(ListCommunitiesResponse { diff --git a/server/src/api/post.rs b/server/src/api/post.rs index e1c9b778f..5b41b4267 100644 --- a/server/src/api/post.rs +++ b/server/src/api/post.rs @@ -178,17 +178,11 @@ impl Perform for Oper { Err(_e) => return Err(APIError::err(&self.op, "couldnt_find_post"))?, }; - let comments = CommentView::list( - &conn, - &SortType::New, - Some(data.id), - None, - None, - user_id, - false, - None, - Some(9999), - )?; + let comments = CommentQueryBuilder::create(&conn) + .for_post_id(data.id) + .my_user_id_optional(user_id) + .limit(9999) + .list()?; let community = CommunityView::read(&conn, post_view.community_id, user_id)?; diff --git a/server/src/api/site.rs b/server/src/api/site.rs index 0e780b384..e2963d8c6 100644 --- a/server/src/api/site.rs +++ b/server/src/api/site.rs @@ -329,31 +329,28 @@ impl Perform for Oper { .list()?; } SearchType::Comments => { - comments = CommentView::list( - &conn, - &sort, - None, - None, - Some(data.q.to_owned()), - None, - false, - data.page, - data.limit, - )?; + comments = CommentQueryBuilder::create(&conn) + .sort(&sort) + .search_term(data.q.to_owned()) + .page_optional(data.page) + .limit_optional(data.limit) + .list()?; } SearchType::Communities => { - communities = CommunityView::list( - &conn, - &sort, - None, - true, - Some(data.q.to_owned()), - data.page, - data.limit, - )?; + communities = CommunityQueryBuilder::create(&conn) + .sort(&sort) + .search_term(data.q.to_owned()) + .page_optional(data.page) + .limit_optional(data.limit) + .list()?; } SearchType::Users => { - users = UserView::list(&conn, &sort, Some(data.q.to_owned()), data.page, data.limit)?; + users = UserQueryBuilder::create(&conn) + .sort(&sort) + .search_term(data.q.to_owned()) + .page_optional(data.page) + .limit_optional(data.limit) + .list()?; } SearchType::All => { posts = PostQueryBuilder::create(&conn) @@ -365,27 +362,26 @@ impl Perform for Oper { .limit_optional(data.limit) .list()?; - comments = CommentView::list( - &conn, - &sort, - None, - None, - Some(data.q.to_owned()), - None, - false, - data.page, - data.limit, - )?; - communities = CommunityView::list( - &conn, - &sort, - None, - true, - Some(data.q.to_owned()), - data.page, - data.limit, - )?; - users = UserView::list(&conn, &sort, Some(data.q.to_owned()), data.page, data.limit)?; + comments = CommentQueryBuilder::create(&conn) + .sort(&sort) + .search_term(data.q.to_owned()) + .page_optional(data.page) + .limit_optional(data.limit) + .list()?; + + communities = CommunityQueryBuilder::create(&conn) + .sort(&sort) + .search_term(data.q.to_owned()) + .page_optional(data.page) + .limit_optional(data.limit) + .list()?; + + users = UserQueryBuilder::create(&conn) + .sort(&sort) + .search_term(data.q.to_owned()) + .page_optional(data.page) + .limit_optional(data.limit) + .list()?; } SearchType::Url => { posts = PostQueryBuilder::create(&conn) diff --git a/server/src/api/user.rs b/server/src/api/user.rs index e3d67451b..7f7af813c 100644 --- a/server/src/api/user.rs +++ b/server/src/api/user.rs @@ -375,38 +375,22 @@ impl Perform for Oper { .page_optional(data.page) .limit_optional(data.limit); + let mut comments_query = CommentQueryBuilder::create(&conn) + .sort(&sort) + .saved_only(data.saved_only) + .my_user_id_optional(user_id) + .page_optional(data.page) + .limit_optional(data.limit); + // If its saved only, you don't care what creator it was + // Or, if its not saved, then you only want it for that specific creator if !data.saved_only { posts_query = posts_query.for_creator_id(user_details_id); + comments_query = comments_query.for_creator_id(user_details_id); } let posts = posts_query.list()?; - - let comments = if data.saved_only { - CommentView::list( - &conn, - &sort, - None, - None, - None, - Some(user_details_id), - data.saved_only, - data.page, - data.limit, - )? - } else { - CommentView::list( - &conn, - &sort, - None, - Some(user_details_id), - None, - user_id, - data.saved_only, - data.page, - data.limit, - )? - }; + let comments = comments_query.list()?; let follows = CommunityFollowerView::for_user(&conn, user_details_id)?; let moderates = CommunityModeratorView::for_user(&conn, user_details_id)?; @@ -569,14 +553,12 @@ impl Perform for Oper { let sort = SortType::from_str(&data.sort)?; - let replies = ReplyView::get_replies( - &conn, - user_id, - &sort, - data.unread_only, - data.page, - data.limit, - )?; + let replies = ReplyQueryBuilder::create(&conn, user_id) + .sort(&sort) + .unread_only(data.unread_only) + .page_optional(data.page) + .limit_optional(data.limit) + .list()?; Ok(GetRepliesResponse { op: self.op.to_string(), @@ -599,14 +581,12 @@ impl Perform for Oper { let sort = SortType::from_str(&data.sort)?; - let mentions = UserMentionView::get_mentions( - &conn, - user_id, - &sort, - data.unread_only, - data.page, - data.limit, - )?; + let mentions = UserMentionQueryBuilder::create(&conn, user_id) + .sort(&sort) + .unread_only(data.unread_only) + .page_optional(data.page) + .limit_optional(data.limit) + .list()?; Ok(GetUserMentionsResponse { op: self.op.to_string(), @@ -662,7 +642,11 @@ impl Perform for Oper { let user_id = claims.id; - let replies = ReplyView::get_replies(&conn, user_id, &SortType::New, true, Some(1), Some(999))?; + let replies = ReplyQueryBuilder::create(&conn, user_id) + .unread_only(true) + .page(1) + .limit(999) + .list()?; for reply in &replies { let comment_form = CommentForm { @@ -683,8 +667,11 @@ impl Perform for Oper { } // Mentions - let mentions = - UserMentionView::get_mentions(&conn, user_id, &SortType::New, true, Some(1), Some(999))?; + let mentions = UserMentionQueryBuilder::create(&conn, user_id) + .unread_only(true) + .page(1) + .limit(999) + .list()?; for mention in &mentions { let mention_form = UserMentionForm { @@ -728,17 +715,10 @@ impl Perform for Oper { } // Comments - let comments = CommentView::list( - &conn, - &SortType::New, - None, - Some(user_id), - None, - None, - false, - None, - Some(std::i64::MAX), - )?; + let comments = CommentQueryBuilder::create(&conn) + .for_creator_id(user_id) + .limit(std::i64::MAX) + .list()?; for comment in &comments { let comment_form = CommentForm { diff --git a/server/src/db/comment_view.rs b/server/src/db/comment_view.rs index 9f7544731..09b864b3e 100644 --- a/server/src/db/comment_view.rs +++ b/server/src/db/comment_view.rs @@ -1,4 +1,5 @@ use super::*; +use diesel::pg::Pg; // The faked schema since diesel doesn't do views table! { @@ -53,48 +54,138 @@ pub struct CommentView { pub saved: Option, } -impl CommentView { - pub fn list( - conn: &PgConnection, - sort: &SortType, - for_post_id: Option, - for_creator_id: Option, - search_term: Option, - my_user_id: Option, - saved_only: bool, - page: Option, - limit: Option, - ) -> Result, Error> { +pub struct CommentQueryBuilder<'a> { + conn: &'a PgConnection, + query: super::comment_view::comment_view::BoxedQuery<'a, Pg>, + sort: &'a SortType, + for_post_id: Option, + for_creator_id: Option, + search_term: Option, + my_user_id: Option, + saved_only: bool, + page: Option, + limit: Option, +} + +impl<'a> CommentQueryBuilder<'a> { + pub fn create(conn: &'a PgConnection) -> Self { use super::comment_view::comment_view::dsl::*; - let (limit, offset) = limit_and_offset(page, limit); + let query = comment_view.into_boxed(); - let mut query = comment_view.into_boxed(); + CommentQueryBuilder { + conn, + query, + sort: &SortType::New, + for_post_id: None, + for_creator_id: None, + search_term: None, + my_user_id: None, + saved_only: false, + page: None, + limit: None, + } + } + + pub fn sort(mut self, sort: &'a SortType) -> Self { + self.sort = sort; + self + } + + pub fn for_post_id(mut self, for_post_id: i32) -> Self { + self.for_post_id = Some(for_post_id); + self + } + + pub fn for_post_id_optional(mut self, for_post_id: Option) -> Self { + self.for_post_id = for_post_id; + self + } + + pub fn for_creator_id(mut self, for_creator_id: i32) -> Self { + self.for_creator_id = Some(for_creator_id); + self + } + + pub fn for_creator_id_optional(mut self, for_creator_id: Option) -> Self { + self.for_creator_id = for_creator_id; + self + } + + pub fn search_term(mut self, search_term: String) -> Self { + self.search_term = Some(search_term); + self + } + + pub fn search_term_optional(mut self, search_term: Option) -> Self { + self.search_term = search_term; + self + } + + pub fn my_user_id(mut self, my_user_id: i32) -> Self { + self.my_user_id = Some(my_user_id); + self + } + + pub fn my_user_id_optional(mut self, my_user_id: Option) -> Self { + self.my_user_id = my_user_id; + self + } + + pub fn saved_only(mut self, saved_only: bool) -> Self { + self.saved_only = saved_only; + self + } + + pub fn page(mut self, page: i64) -> Self { + self.page = Some(page); + self + } + + pub fn page_optional(mut self, page: Option) -> Self { + self.page = page; + self + } + + pub fn limit(mut self, limit: i64) -> Self { + self.limit = Some(limit); + self + } + + pub fn limit_optional(mut self, limit: Option) -> Self { + self.limit = limit; + self + } + + pub fn list(self) -> Result, Error> { + use super::comment_view::comment_view::dsl::*; + + let mut query = self.query; // The view lets you pass a null user_id, if you're not logged in - if let Some(my_user_id) = my_user_id { + if let Some(my_user_id) = self.my_user_id { query = query.filter(user_id.eq(my_user_id)); } else { query = query.filter(user_id.is_null()); } - if let Some(for_creator_id) = for_creator_id { + if let Some(for_creator_id) = self.for_creator_id { query = query.filter(creator_id.eq(for_creator_id)); }; - if let Some(for_post_id) = for_post_id { + if let Some(for_post_id) = self.for_post_id { query = query.filter(post_id.eq(for_post_id)); }; - if let Some(search_term) = search_term { + if let Some(search_term) = self.search_term { query = query.filter(content.ilike(fuzzy_search(&search_term))); }; - if saved_only { + if self.saved_only { query = query.filter(saved.eq(true)); } - query = match sort { + query = match self.sort { // SortType::Hot => query.order(hot_rank.desc(), published.desc()), SortType::New => query.order_by(published.desc()), SortType::TopAll => query.order_by(score.desc()), @@ -113,10 +204,17 @@ impl CommentView { _ => query.order_by(published.desc()), }; - // Note: deleted and removed comments are done on the front side - query.limit(limit).offset(offset).load::(conn) - } + let (limit, offset) = limit_and_offset(self.page, self.limit); + // Note: deleted and removed comments are done on the front side + query + .limit(limit) + .offset(offset) + .load::(self.conn) + } +} + +impl CommentView { pub fn read( conn: &PgConnection, from_comment_id: i32, @@ -196,30 +294,77 @@ pub struct ReplyView { pub recipient_id: i32, } -impl ReplyView { - pub fn get_replies( - conn: &PgConnection, - for_user_id: i32, - sort: &SortType, - unread_only: bool, - page: Option, - limit: Option, - ) -> Result, Error> { +pub struct ReplyQueryBuilder<'a> { + conn: &'a PgConnection, + query: super::comment_view::reply_view::BoxedQuery<'a, Pg>, + for_user_id: i32, + sort: &'a SortType, + unread_only: bool, + page: Option, + limit: Option, +} + +impl<'a> ReplyQueryBuilder<'a> { + pub fn create(conn: &'a PgConnection, for_user_id: i32) -> Self { use super::comment_view::reply_view::dsl::*; - let (limit, offset) = limit_and_offset(page, limit); + let query = reply_view.into_boxed(); - let mut query = reply_view.into_boxed(); + ReplyQueryBuilder { + conn, + query, + for_user_id: for_user_id, + sort: &SortType::New, + unread_only: false, + page: None, + limit: None, + } + } + + pub fn sort(mut self, sort: &'a SortType) -> Self { + self.sort = sort; + self + } + + pub fn unread_only(mut self, unread_only: bool) -> Self { + self.unread_only = unread_only; + self + } + + pub fn page(mut self, page: i64) -> Self { + self.page = Some(page); + self + } + + pub fn page_optional(mut self, page: Option) -> Self { + self.page = page; + self + } + + pub fn limit(mut self, limit: i64) -> Self { + self.limit = Some(limit); + self + } + + pub fn limit_optional(mut self, limit: Option) -> Self { + self.limit = limit; + self + } + + pub fn list(self) -> Result, Error> { + use super::comment_view::reply_view::dsl::*; + + let mut query = self.query; query = query - .filter(user_id.eq(for_user_id)) - .filter(recipient_id.eq(for_user_id)); + .filter(user_id.eq(self.for_user_id)) + .filter(recipient_id.eq(self.for_user_id)); - if unread_only { + if self.unread_only { query = query.filter(read.eq(false)); } - query = match sort { + query = match self.sort { // SortType::Hot => query.order_by(hot_rank.desc()), SortType::New => query.order_by(published.desc()), SortType::TopAll => query.order_by(score.desc()), @@ -238,7 +383,11 @@ impl ReplyView { _ => query.order_by(published.desc()), }; - query.limit(limit).offset(offset).load::(conn) + let (limit, offset) = limit_and_offset(self.page, self.limit); + query + .limit(limit) + .offset(offset) + .load::(self.conn) } } @@ -368,30 +517,16 @@ mod tests { saved: None, }; - let read_comment_views_no_user = CommentView::list( - &conn, - &SortType::New, - Some(inserted_post.id), - None, - None, - None, - false, - None, - None, - ) - .unwrap(); - let read_comment_views_with_user = CommentView::list( - &conn, - &SortType::New, - Some(inserted_post.id), - None, - None, - Some(inserted_user.id), - false, - None, - None, - ) - .unwrap(); + let read_comment_views_no_user = CommentQueryBuilder::create(&conn) + .for_post_id(inserted_post.id) + .list() + .unwrap(); + let read_comment_views_with_user = CommentQueryBuilder::create(&conn) + .for_post_id(inserted_post.id) + .for_creator_id(inserted_user.id) + .list() + .unwrap(); + let like_removed = CommentLike::remove(&conn, &comment_like_form).unwrap(); let num_deleted = Comment::delete(&conn, inserted_comment.id).unwrap(); Post::delete(&conn, inserted_post.id).unwrap(); diff --git a/server/src/db/community_view.rs b/server/src/db/community_view.rs index dabf01bcb..c90beaba5 100644 --- a/server/src/db/community_view.rs +++ b/server/src/db/community_view.rs @@ -1,4 +1,6 @@ +use super::community_view::community_view::BoxedQuery; use super::*; +use diesel::pg::Pg; table! { community_view (id) { @@ -99,6 +101,134 @@ pub struct CommunityView { pub subscribed: Option, } +pub struct CommunityQueryBuilder<'a> { + conn: &'a PgConnection, + query: BoxedQuery<'a, Pg>, + sort: &'a SortType, + from_user_id: Option, + show_nsfw: bool, + search_term: Option, + page: Option, + limit: Option, +} + +impl<'a> CommunityQueryBuilder<'a> { + pub fn create(conn: &'a PgConnection) -> Self { + use super::community_view::community_view::dsl::*; + + let query = community_view.into_boxed(); + + CommunityQueryBuilder { + conn, + query, + sort: &SortType::Hot, + from_user_id: None, + show_nsfw: true, + search_term: None, + page: None, + limit: None, + } + } + + pub fn sort(mut self, sort: &'a SortType) -> Self { + self.sort = sort; + self + } + + pub fn from_user_id(mut self, from_user_id: i32) -> Self { + self.from_user_id = Some(from_user_id); + self + } + + pub fn from_user_id_optional(self, from_user_id: Option) -> Self { + match from_user_id { + Some(from_user_id) => self.from_user_id(from_user_id), + None => self, + } + } + + pub fn show_nsfw(mut self, show_nsfw: bool) -> Self { + self.show_nsfw = show_nsfw; + self + } + + pub fn search_term(mut self, search_term: String) -> Self { + self.search_term = Some(search_term); + self + } + + pub fn search_term_optional(mut self, search_term: Option) -> Self { + self.search_term = search_term; + self + } + + pub fn page(mut self, page: i64) -> Self { + self.page = Some(page); + self + } + + pub fn page_optional(mut self, page: Option) -> Self { + self.page = page; + self + } + + pub fn limit(mut self, limit: i64) -> Self { + self.limit = Some(limit); + self + } + + pub fn limit_optional(mut self, limit: Option) -> Self { + self.limit = limit; + self + } + + pub fn list(self) -> Result, Error> { + use super::community_view::community_view::dsl::*; + + let mut query = self.query; + + if let Some(search_term) = self.search_term { + query = query.filter(name.ilike(fuzzy_search(&search_term))); + }; + + // The view lets you pass a null user_id, if you're not logged in + match self.sort { + SortType::Hot => { + query = query + .order_by(hot_rank.desc()) + .then_order_by(number_of_subscribers.desc()) + .filter(user_id.is_null()) + } + SortType::New => query = query.order_by(published.desc()).filter(user_id.is_null()), + SortType::TopAll => match self.from_user_id { + Some(from_user_id) => { + query = query + .filter(user_id.eq(from_user_id)) + .order_by((subscribed.asc(), number_of_subscribers.desc())) + } + None => { + query = query + .order_by(number_of_subscribers.desc()) + .filter(user_id.is_null()) + } + }, + _ => (), + }; + + if !self.show_nsfw { + query = query.filter(nsfw.eq(false)); + }; + + let (limit, offset) = limit_and_offset(self.page, self.limit); + query + .limit(limit) + .offset(offset) + .filter(removed.eq(false)) + .filter(deleted.eq(false)) + .load::(self.conn) + } +} + impl CommunityView { pub fn read( conn: &PgConnection, @@ -120,60 +250,6 @@ impl CommunityView { query.first::(conn) } - - pub fn list( - conn: &PgConnection, - sort: &SortType, - from_user_id: Option, - show_nsfw: bool, - search_term: Option, - page: Option, - limit: Option, - ) -> Result, Error> { - use super::community_view::community_view::dsl::*; - let mut query = community_view.into_boxed(); - - let (limit, offset) = limit_and_offset(page, limit); - - if let Some(search_term) = search_term { - query = query.filter(name.ilike(fuzzy_search(&search_term))); - }; - - // The view lets you pass a null user_id, if you're not logged in - match sort { - SortType::Hot => { - query = query - .order_by(hot_rank.desc()) - .then_order_by(number_of_subscribers.desc()) - .filter(user_id.is_null()) - } - SortType::New => query = query.order_by(published.desc()).filter(user_id.is_null()), - SortType::TopAll => match from_user_id { - Some(from_user_id) => { - query = query - .filter(user_id.eq(from_user_id)) - .order_by((subscribed.asc(), number_of_subscribers.desc())) - } - None => { - query = query - .order_by(number_of_subscribers.desc()) - .filter(user_id.is_null()) - } - }, - _ => (), - }; - - if !show_nsfw { - query = query.filter(nsfw.eq(false)); - }; - - query - .limit(limit) - .offset(offset) - .filter(removed.eq(false)) - .filter(deleted.eq(false)) - .load::(conn) - } } #[derive( diff --git a/server/src/db/user_mention_view.rs b/server/src/db/user_mention_view.rs index 6676ab9ab..19b803da8 100644 --- a/server/src/db/user_mention_view.rs +++ b/server/src/db/user_mention_view.rs @@ -1,4 +1,6 @@ +use super::user_mention_view::user_mention_view::BoxedQuery; use super::*; +use diesel::pg::Pg; // The faked schema since diesel doesn't do views table! { @@ -57,30 +59,77 @@ pub struct UserMentionView { pub recipient_id: i32, } -impl UserMentionView { - pub fn get_mentions( - conn: &PgConnection, - for_user_id: i32, - sort: &SortType, - unread_only: bool, - page: Option, - limit: Option, - ) -> Result, Error> { +pub struct UserMentionQueryBuilder<'a> { + conn: &'a PgConnection, + query: BoxedQuery<'a, Pg>, + for_user_id: i32, + sort: &'a SortType, + unread_only: bool, + page: Option, + limit: Option, +} + +impl<'a> UserMentionQueryBuilder<'a> { + pub fn create(conn: &'a PgConnection, for_user_id: i32) -> Self { use super::user_mention_view::user_mention_view::dsl::*; - let (limit, offset) = limit_and_offset(page, limit); + let query = user_mention_view.into_boxed(); - let mut query = user_mention_view.into_boxed(); + UserMentionQueryBuilder { + conn, + query, + for_user_id: for_user_id, + sort: &SortType::New, + unread_only: false, + page: None, + limit: None, + } + } - query = query - .filter(user_id.eq(for_user_id)) - .filter(recipient_id.eq(for_user_id)); + pub fn sort(mut self, sort: &'a SortType) -> Self { + self.sort = sort; + self + } - if unread_only { + pub fn unread_only(mut self, unread_only: bool) -> Self { + self.unread_only = unread_only; + self + } + + pub fn page(mut self, page: i64) -> Self { + self.page = Some(page); + self + } + + pub fn page_optional(mut self, page: Option) -> Self { + self.page = page; + self + } + + pub fn limit(mut self, limit: i64) -> Self { + self.limit = Some(limit); + self + } + + pub fn limit_optional(mut self, limit: Option) -> Self { + self.limit = limit; + self + } + + pub fn list(self) -> Result, Error> { + use super::user_mention_view::user_mention_view::dsl::*; + + let mut query = self.query; + + if self.unread_only { query = query.filter(read.eq(false)); } - query = match sort { + query = query + .filter(user_id.eq(self.for_user_id)) + .filter(recipient_id.eq(self.for_user_id)); + + query = match self.sort { // SortType::Hot => query.order_by(hot_rank.desc()), SortType::New => query.order_by(published.desc()), SortType::TopAll => query.order_by(score.desc()), @@ -99,9 +148,15 @@ impl UserMentionView { _ => query.order_by(published.desc()), }; - query.limit(limit).offset(offset).load::(conn) + let (limit, offset) = limit_and_offset(self.page, self.limit); + query + .limit(limit) + .offset(offset) + .load::(self.conn) } +} +impl UserMentionView { pub fn read( conn: &PgConnection, from_user_mention_id: i32, diff --git a/server/src/db/user_view.rs b/server/src/db/user_view.rs index 028cd07c1..0637d647e 100644 --- a/server/src/db/user_view.rs +++ b/server/src/db/user_view.rs @@ -1,4 +1,6 @@ +use super::user_view::user_view::BoxedQuery; use super::*; +use diesel::pg::Pg; table! { user_view (id) { @@ -32,25 +34,73 @@ pub struct UserView { pub comment_score: i64, } -impl UserView { - pub fn list( - conn: &PgConnection, - sort: &SortType, - search_term: Option, - page: Option, - limit: Option, - ) -> Result, Error> { +pub struct UserQueryBuilder<'a> { + conn: &'a PgConnection, + query: BoxedQuery<'a, Pg>, + sort: &'a SortType, + page: Option, + limit: Option, +} + +impl<'a> UserQueryBuilder<'a> { + pub fn create(conn: &'a PgConnection) -> Self { use super::user_view::user_view::dsl::*; - let (limit, offset) = limit_and_offset(page, limit); + let query = user_view.into_boxed(); - let mut query = user_view.into_boxed(); + UserQueryBuilder { + conn, + query, + sort: &SortType::Hot, + page: None, + limit: None, + } + } - if let Some(search_term) = search_term { - query = query.filter(name.ilike(fuzzy_search(&search_term))); - }; + pub fn sort(mut self, sort: &'a SortType) -> Self { + self.sort = sort; + self + } - query = match sort { + pub fn search_term(mut self, search_term: String) -> Self { + use super::user_view::user_view::dsl::*; + self.query = self.query.filter(name.ilike(fuzzy_search(&search_term))); + self + } + + pub fn search_term_optional(self, search_term: Option) -> Self { + match search_term { + Some(search_term) => self.search_term(search_term), + None => self, + } + } + + pub fn page(mut self, page: i64) -> Self { + self.page = Some(page); + self + } + + pub fn page_optional(mut self, page: Option) -> Self { + self.page = page; + self + } + + pub fn limit(mut self, limit: i64) -> Self { + self.limit = Some(limit); + self + } + + pub fn limit_optional(mut self, limit: Option) -> Self { + self.limit = limit; + self + } + + pub fn list(self) -> Result, Error> { + use super::user_view::user_view::dsl::*; + + let mut query = self.query; + + query = match self.sort { SortType::Hot => query .order_by(comment_score.desc()) .then_order_by(published.desc()), @@ -70,11 +120,14 @@ impl UserView { .order_by(comment_score.desc()), }; + let (limit, offset) = limit_and_offset(self.page, self.limit); query = query.limit(limit).offset(offset); - query.load::(conn) + query.load::(self.conn) } +} +impl UserView { pub fn read(conn: &PgConnection, from_user_id: i32) -> Result { use super::user_view::user_view::dsl::*; diff --git a/server/src/feeds.rs b/server/src/feeds.rs index 82ad3526f..810f6d596 100644 --- a/server/src/feeds.rs +++ b/server/src/feeds.rs @@ -1,12 +1,12 @@ extern crate rss; use super::*; -use crate::db::comment_view::ReplyView; +use crate::db::comment_view::{ReplyQueryBuilder, ReplyView}; use crate::db::community::Community; use crate::db::community_view::SiteView; use crate::db::post_view::{PostQueryBuilder, PostView}; use crate::db::user::User_; -use crate::db::user_mention_view::UserMentionView; +use crate::db::user_mention_view::{UserMentionQueryBuilder, UserMentionView}; use crate::db::{establish_connection, ListingType, SortType}; use crate::Settings; use actix_web::body::Body; @@ -193,9 +193,13 @@ fn get_feed_inbox(jwt: String) -> Result { let sort = SortType::New; - let replies = ReplyView::get_replies(&conn, user_id, &sort, false, None, None)?; + let replies = ReplyQueryBuilder::create(&conn, user_id) + .sort(&sort) + .list()?; - let mentions = UserMentionView::get_mentions(&conn, user_id, &sort, false, None, None)?; + let mentions = UserMentionQueryBuilder::create(&conn, user_id) + .sort(&sort) + .list()?; let items = create_reply_and_mention_items(replies, mentions);