From 1053df1a4b5ca81625b4917fe6bcb198d633c7a9 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Fri, 6 Dec 2024 09:49:11 -0500 Subject: [PATCH] Adding views and replaceable schema. --- crates/api_common/src/person.rs | 19 +- crates/apub/src/api/read_person.rs | 71 +++---- .../db_schema/replaceable_schema/triggers.sql | 28 +++ crates/db_views/src/profile_combined_view.rs | 190 ++++++++---------- crates/db_views/src/structs.rs | 5 +- .../down.sql | 1 + .../up.sql | 3 +- 7 files changed, 155 insertions(+), 162 deletions(-) diff --git a/crates/api_common/src/person.rs b/crates/api_common/src/person.rs index 797946d65..0b51c7453 100644 --- a/crates/api_common/src/person.rs +++ b/crates/api_common/src/person.rs @@ -7,7 +7,11 @@ use lemmy_db_schema::{ PostListingMode, PostSortType, }; -use lemmy_db_views::structs::{CommentView, LocalImageView, PostView}; +use lemmy_db_views::structs::{ + LocalImageView, + ProfileCombinedPaginationCursor, + ProfileCombinedView, +}; use lemmy_db_views_actor::structs::{ CommentReplyView, CommunityModeratorView, @@ -223,15 +227,13 @@ pub struct GetPersonDetails { #[cfg_attr(feature = "full", ts(optional))] pub username: Option, #[cfg_attr(feature = "full", ts(optional))] - pub sort: Option, - #[cfg_attr(feature = "full", ts(optional))] - pub page: Option, - #[cfg_attr(feature = "full", ts(optional))] - pub limit: Option, - #[cfg_attr(feature = "full", ts(optional))] pub community_id: Option, #[cfg_attr(feature = "full", ts(optional))] pub saved_only: Option, + #[cfg_attr(feature = "full", ts(optional))] + pub page_cursor: Option, + #[cfg_attr(feature = "full", ts(optional))] + pub page_back: Option, } #[skip_serializing_none] @@ -243,8 +245,7 @@ pub struct GetPersonDetailsResponse { pub person_view: PersonView, #[cfg_attr(feature = "full", ts(optional))] pub site: Option, - pub comments: Vec, - pub posts: Vec, + pub content: Vec, pub moderates: Vec, } diff --git a/crates/apub/src/api/read_person.rs b/crates/apub/src/api/read_person.rs index fac68cd63..ba3160386 100644 --- a/crates/apub/src/api/read_person.rs +++ b/crates/apub/src/api/read_person.rs @@ -6,10 +6,9 @@ use lemmy_api_common::{ person::{GetPersonDetails, GetPersonDetailsResponse}, utils::{check_private_instance, read_site_for_actor}, }; -use lemmy_db_schema::{source::person::Person, utils::post_to_comment_sort_type}; +use lemmy_db_schema::source::person::Person; use lemmy_db_views::{ - comment_view::CommentQuery, - post_view::PostQuery, + profile_combined_view::ProfileCombinedQuery, structs::{LocalUserView, SiteView}, }; use lemmy_db_views_actor::structs::{CommunityModeratorView, PersonView}; @@ -47,46 +46,38 @@ pub async fn read_person( // `my_user` let person_view = PersonView::read(&mut context.pool(), person_details_id).await?; - let sort = data.sort; - let page = data.page; - let limit = data.limit; - let saved_only = data.saved_only; - let community_id = data.community_id; - // 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 - let creator_id = if !saved_only.unwrap_or_default() { - Some(person_details_id) + // parse pagination token + let page_after = if let Some(pa) = &data.page_cursor { + Some(pa.read(&mut context.pool()).await?) } else { None }; + let page_back = data.page_back; + let saved_only = data.saved_only; + let community_id = data.community_id; - let local_user = local_user_view.as_ref().map(|l| &l.local_user); + // If its saved only, then ignore the person details id, + // and use your local user's id + let creator_id = if !saved_only.unwrap_or_default() { + Some(person_details_id) + } else { + local_user_view.as_ref().map(|u| u.local_user.person_id) + }; - let posts = PostQuery { - sort, - saved_only, - local_user, - community_id, - page, - limit, - creator_id, - ..Default::default() - } - .list(&local_site.site, &mut context.pool()) - .await?; - - let comments = CommentQuery { - local_user, - sort: sort.map(post_to_comment_sort_type), - saved_only, - community_id, - page, - limit, - creator_id, - ..Default::default() - } - .list(&local_site.site, &mut context.pool()) - .await?; + let content = if let Some(creator_id) = creator_id { + ProfileCombinedQuery { + creator_id, + community_id, + saved_only, + page_after, + page_back, + } + .list(&mut context.pool(), &local_user_view) + .await? + } else { + // if the creator is missing (saved_only, and no local_user), then return empty content + Vec::new() + }; let moderates = CommunityModeratorView::for_person( &mut context.pool(), @@ -97,12 +88,10 @@ pub async fn read_person( let site = read_site_for_actor(person_view.person.actor_id.clone(), &context).await?; - // Return the jwt Ok(Json(GetPersonDetailsResponse { person_view, site, moderates, - comments, - posts, + content, })) } diff --git a/crates/db_schema/replaceable_schema/triggers.sql b/crates/db_schema/replaceable_schema/triggers.sql index 2d9b0df6e..596a86eeb 100644 --- a/crates/db_schema/replaceable_schema/triggers.sql +++ b/crates/db_schema/replaceable_schema/triggers.sql @@ -685,3 +685,31 @@ CALL r.create_report_combined_trigger ('comment_report'); CALL r.create_report_combined_trigger ('private_message_report'); +-- Profile (comment, post) +CREATE PROCEDURE r.create_profile_combined_trigger (table_name text) +LANGUAGE plpgsql +AS $a$ +BEGIN + EXECUTE replace($b$ CREATE FUNCTION r.profile_combined_thing_insert ( ) + RETURNS TRIGGER + LANGUAGE plpgsql + AS $$ + BEGIN + INSERT INTO profile_combined (published, thing_id) + VALUES (NEW.published, NEW.id); + RETURN NEW; + END $$; + CREATE TRIGGER profile_combined + AFTER INSERT ON thing + FOR EACH ROW + EXECUTE FUNCTION r.profile_combined_thing_insert ( ); + $b$, + 'thing', + table_name); +END; +$a$; + +CALL r.create_profile_combined_trigger ('post'); + +CALL r.create_profile_combined_trigger ('comment'); + diff --git a/crates/db_views/src/profile_combined_view.rs b/crates/db_views/src/profile_combined_view.rs index 59d218025..d62b9315c 100644 --- a/crates/db_views/src/profile_combined_view.rs +++ b/crates/db_views/src/profile_combined_view.rs @@ -18,14 +18,15 @@ use diesel::{ use diesel_async::RunQueryDsl; use i_love_jesus::PaginatedQueryBuilder; use lemmy_db_schema::{ - aliases::{self, creator_community_actions}, - newtypes::CommunityId, + aliases::creator_community_actions, + newtypes::{CommunityId, PersonId}, schema::{ comment, comment_actions, comment_aggregates, community, community_actions, + image_details, local_user, person, person_actions, @@ -74,11 +75,11 @@ impl ProfileCombinedPaginationCursor { #[derive(Clone)] pub struct PaginationCursorData(ProfileCombined); -// TODO check these #[derive(Default)] pub struct ProfileCombinedQuery { + pub creator_id: PersonId, pub community_id: Option, - pub unresolved_only: Option, + pub saved_only: Option, pub page_after: Option, pub page_back: Option, } @@ -87,10 +88,12 @@ impl ProfileCombinedQuery { pub async fn list( self, pool: &mut DbPool<'_>, - user: &LocalUserView, + user: &Option, ) -> LemmyResult> { - let my_person_id = user.local_user.person_id; - // let item_creator = aliases::person1.field(person::id); + let my_person_id = user + .as_ref() + .map(|u| u.local_user.person_id) + .unwrap_or(PersonId(-1)); let item_creator = person::id; let conn = &mut get_conn(pool).await?; @@ -114,20 +117,11 @@ impl ProfileCombinedQuery { // The item creator .inner_join( person::table.on( - post::creator_id + comment::creator_id .eq(person::id) - .or(comment::creator_id.eq(person::id)), + .or(post::creator_id.eq(person::id)), ), ) - // The item creator - // You can now use aliases::person1.field(person::id) / item_creator for all the item actions - // .inner_join( - // aliases::person1.on( - // post::creator_id - // .eq(item_creator) - // .or(comment::creator_id.eq(item_creator)), - // ), - // ) // The community .inner_join(community::table.on(post::community_id.eq(community::id))) .left_join(actions_alias( @@ -153,10 +147,7 @@ impl ProfileCombinedQuery { Some(my_person_id), item_creator, )) - .left_join( - post_aggregates::table - .on(profile_combined::post_id.eq(post_aggregates::post_id.nullable())), - ) + .inner_join(post_aggregates::table.on(post::id.eq(post_aggregates::post_id))) .left_join( comment_aggregates::table .on(profile_combined::comment_id.eq(comment_aggregates::comment_id.nullable())), @@ -166,43 +157,42 @@ impl ProfileCombinedQuery { Some(my_person_id), comment::id, )) + .left_join(image_details::table.on(post::thumbnail_url.eq(image_details::link.nullable()))) + // The creator id filter + .filter(item_creator.eq(self.creator_id)) .select(( // Post-specific - post::all_columns.nullable(), - // post_aggregates::all_columns.nullable(), - // coalesce( - // post_aggregates::comments.nullable() - post_actions::read_comments_amount.nullable(), - // post_aggregates::comments, - // ) - // .nullable(), - // post_actions::saved.nullable().is_not_null(), - // post_actions::read.nullable().is_not_null(), - // post_actions::hidden.nullable().is_not_null(), - // post_actions::like_score.nullable(), - // // Comment-specific - // comment::all_columns.nullable(), - // comment_aggregates::all_columns.nullable(), - // comment_actions::saved.nullable().is_not_null(), - // comment_actions::like_score.nullable(), - // // Private-message-specific - // private_message_profile::all_columns.nullable(), - // private_message::all_columns.nullable(), - // // Shared - // person::all_columns, - // aliases::person1.fields(person::all_columns), - // community::all_columns.nullable(), - // CommunityFollower::select_subscribed_type(), - // aliases::person2.fields(person::all_columns.nullable()), - // local_user::admin.nullable().is_not_null(), - // creator_community_actions - // .field(community_actions::received_ban) - // .nullable() - // .is_not_null(), - // creator_community_actions - // .field(community_actions::became_moderator) - // .nullable() - // .is_not_null(), - // person_actions::blocked.nullable().is_not_null(), + post_aggregates::all_columns, + coalesce( + post_aggregates::comments.nullable() - post_actions::read_comments_amount.nullable(), + post_aggregates::comments, + ), + post_actions::saved.nullable().is_not_null(), + post_actions::read.nullable().is_not_null(), + post_actions::hidden.nullable().is_not_null(), + post_actions::like_score.nullable(), + image_details::all_columns.nullable(), + // Comment-specific + comment::all_columns.nullable(), + comment_aggregates::all_columns.nullable(), + comment_actions::saved.nullable().is_not_null(), + comment_actions::like_score.nullable(), + // Shared + post::all_columns, + community::all_columns, + person::all_columns, + CommunityFollower::select_subscribed_type(), + local_user::admin.nullable().is_not_null(), + creator_community_actions + .field(community_actions::became_moderator) + .nullable() + .is_not_null(), + creator_community_actions + .field(community_actions::received_ban) + .nullable() + .is_not_null(), + person_actions::blocked.nullable().is_not_null(), + community_actions::received_ban.nullable().is_not_null(), )) .into_boxed(); @@ -210,9 +200,13 @@ impl ProfileCombinedQuery { query = query.filter(community::id.eq(community_id)); } - // If its not an admin, get only the ones you mod - if !user.local_user.admin { - query = query.filter(community_actions::became_moderator.is_not_null()); + // If its saved only, then filter + if self.saved_only.unwrap_or_default() { + query = query.filter( + comment_actions::saved + .is_not_null() + .or(post_actions::saved.is_not_null()), + ) } let mut query = PaginatedQueryBuilder::new(query); @@ -225,25 +219,11 @@ impl ProfileCombinedQuery { query = query.after(page_after); } - // If viewing all profiles, order by newest, but if viewing unresolved only, show the oldest - // first (FIFO) - if self.unresolved_only.unwrap_or_default() { - query = query - .filter( - post_profile::resolved - .eq(false) - .or(comment_profile::resolved.eq(false)) - .or(private_message_profile::resolved.eq(false)), - ) - // TODO: when a `then_asc` method is added, use it here, make the id sort direction match, - // and remove the separate index; unless additional columns are added to this sort - .then_desc(ReverseTimestampKey(key::published)); - } else { - query = query.then_desc(key::published); - } - - // Tie breaker - query = query.then_desc(key::id); + // Sorting by published + query = query + .then_desc(ReverseTimestampKey(key::published)) + // Tie breaker + .then_desc(key::id); let res = query.load::(conn).await?; @@ -259,14 +239,28 @@ fn map_to_enum(view: ProfileCombinedViewInternal) -> Option // Use for a short alias let v = view; - if let (Some(post), Some(community), Some(unread_comments), Some(counts)) = - (v.post, v.community, v.post_unread_comments, v.post_counts) - { - Some(ProfileCombinedView::Post(PostView { - post, - community, - unread_comments, + if let (Some(comment), Some(counts)) = (v.comment, v.comment_counts) { + Some(ProfileCombinedView::Comment(CommentView { + comment, counts, + post: v.post, + community: v.community, + creator: v.item_creator, + creator_banned_from_community: v.item_creator_banned_from_community, + creator_is_moderator: v.item_creator_is_moderator, + creator_is_admin: v.item_creator_is_admin, + creator_blocked: v.item_creator_blocked, + subscribed: v.subscribed, + saved: v.comment_saved, + my_vote: v.my_comment_vote, + banned_from_community: v.banned_from_community, + })) + } else { + Some(ProfileCombinedView::Post(PostView { + post: v.post, + community: v.community, + unread_comments: v.post_unread_comments, + counts: v.post_counts, creator: v.item_creator, creator_banned_from_community: v.item_creator_banned_from_community, creator_is_moderator: v.item_creator_is_moderator, @@ -277,28 +271,8 @@ fn map_to_enum(view: ProfileCombinedViewInternal) -> Option read: v.post_read, hidden: v.post_hidden, my_vote: v.my_post_vote, + image_details: v.image_details, + banned_from_community: v.banned_from_community, })) - } else if let (Some(comment), Some(counts), Some(post), Some(community)) = ( - v.comment, - v.comment_counts, - v.post.clone(), - v.community.clone(), - ) { - Some(ProfileCombinedView::Comment(CommentView { - comment, - counts, - post, - community, - creator: v.item_creator, - creator_banned_from_community: v.item_creator_banned_from_community, - creator_is_moderator: v.item_creator_is_moderator, - creator_is_admin: v.item_creator_is_admin, - creator_blocked: v.item_creator_blocked, - subscribed: v.subscribed, - saved: v.comment_saved, - my_vote: v.my_comment_vote, - })) - } else { - None } } diff --git a/crates/db_views/src/structs.rs b/crates/db_views/src/structs.rs index 5dd42684b..3ee2fd25c 100644 --- a/crates/db_views/src/structs.rs +++ b/crates/db_views/src/structs.rs @@ -310,8 +310,8 @@ pub struct ProfileCombinedViewInternal { pub my_post_vote: Option, pub image_details: Option, // Comment-specific - pub comment: Comment, - pub comment_counts: CommentAggregates, + pub comment: Option, + pub comment_counts: Option, pub comment_saved: bool, pub my_comment_vote: Option, // Shared @@ -323,7 +323,6 @@ pub struct ProfileCombinedViewInternal { pub item_creator_is_moderator: bool, pub item_creator_banned_from_community: bool, pub item_creator_blocked: bool, - pub item_saved: bool, pub banned_from_community: bool, } diff --git a/migrations/2024-12-05-233704_add_profile_combined_table/down.sql b/migrations/2024-12-05-233704_add_profile_combined_table/down.sql index 9d9a39411..9426ebe38 100644 --- a/migrations/2024-12-05-233704_add_profile_combined_table/down.sql +++ b/migrations/2024-12-05-233704_add_profile_combined_table/down.sql @@ -1 +1,2 @@ DROP TABLE profile_combined; + diff --git a/migrations/2024-12-05-233704_add_profile_combined_table/up.sql b/migrations/2024-12-05-233704_add_profile_combined_table/up.sql index c0d4171f0..183529ee4 100644 --- a/migrations/2024-12-05-233704_add_profile_combined_table/up.sql +++ b/migrations/2024-12-05-233704_add_profile_combined_table/up.sql @@ -4,7 +4,7 @@ CREATE TABLE profile_combined ( id serial PRIMARY KEY, published timestamptz NOT NULL, post_id int UNIQUE REFERENCES post ON UPDATE CASCADE ON DELETE CASCADE, - comment_id int UNIQUE REFERENCES comment ON UPDATE CASCADE ON DELETE CASCADE, + comment_id int UNIQUE REFERENCES COMMENT ON UPDATE CASCADE ON DELETE CASCADE, -- Make sure only one of the columns is not null CHECK ((post_id IS NOT NULL)::integer + (comment_id IS NOT NULL)::integer = 1) ); @@ -27,3 +27,4 @@ SELECT id FROM comment; +