From 047db9ac8590670538129b5f765f47e7c66a8a80 Mon Sep 17 00:00:00 2001 From: Nutomic Date: Thu, 20 Jul 2023 16:36:16 +0200 Subject: [PATCH 1/3] Handle displaying of deleted and removed posts/comments (fixes #2624) (#3286) * Handle displaying of deleted and removed posts/comments (fixes #2624) * remove duplicate test * fix tests * no show_removed/show_deleted * merge * partially fix tests * fix tests * clippy * fix tests * get rid of build_post_response_deleted_allowed --- api_tests/src/comment.spec.ts | 6 +- api_tests/src/post.spec.ts | 8 +- api_tests/src/shared.ts | 12 ++ crates/api_common/src/build_response.rs | 13 -- crates/api_crud/src/post/delete.rs | 4 +- crates/api_crud/src/post/remove.rs | 4 +- crates/apub/src/api/list_comments.rs | 3 +- crates/apub/src/api/list_posts.rs | 11 +- crates/apub/src/api/read_person.rs | 39 ++--- crates/apub/src/api/search.rs | 17 +- crates/db_views/src/comment_view.rs | 120 +++++++++----- crates/db_views/src/post_view.rs | 210 +++++++++++++++++------- crates/routes/src/feeds.rs | 4 +- 13 files changed, 273 insertions(+), 178 deletions(-) diff --git a/api_tests/src/comment.spec.ts b/api_tests/src/comment.spec.ts index 80cb868f2..d1b20d77e 100644 --- a/api_tests/src/comment.spec.ts +++ b/api_tests/src/comment.spec.ts @@ -29,6 +29,7 @@ import { getComments, getCommentParentId, resolveCommunity, + getPersonDetails, } from "./shared"; import { CommentView } from "lemmy-js-client/dist/types/CommentView"; @@ -160,10 +161,11 @@ test("Remove a comment from admin and community on the same instance", async () expect(removeCommentRes.comment_view.comment.removed).toBe(true); // Make sure that comment is removed on alpha (it gets pushed since an admin from beta removed it) - let refetchedPostComments = await getComments( + let refetchedPostComments = await getPersonDetails( alpha, - postRes.post_view.post.id, + commentRes.comment_view.comment.creator_id, ); + console.log(refetchedPostComments.comments[0].comment); expect(refetchedPostComments.comments[0].comment.removed).toBe(true); let unremoveCommentRes = await removeComment(beta, false, betaCommentId); diff --git a/api_tests/src/post.spec.ts b/api_tests/src/post.spec.ts index cabbcfd8a..8c48dc874 100644 --- a/api_tests/src/post.spec.ts +++ b/api_tests/src/post.spec.ts @@ -388,8 +388,8 @@ test("Enforce site ban for federated user", async () => { expect(alphaUserOnBeta1.person?.person.banned).toBe(true); // existing alpha post should be removed on beta - let searchBeta2 = await searchPostLocal(beta, postRes1.post_view.post); - expect(searchBeta2.posts[0].post.removed).toBe(true); + let searchBeta2 = await getPost(beta, searchBeta1.posts[0].post.id); + expect(searchBeta2.post_view.post.removed).toBe(true); // Unban alpha let unBanAlpha = await banPersonFromSite( @@ -436,8 +436,8 @@ test("Enforce community ban for federated user", async () => { expect(banAlpha.banned).toBe(true); // ensure that the post by alpha got removed - let searchAlpha1 = await searchPostLocal(alpha, postRes1.post_view.post); - expect(searchAlpha1.posts[0].post.removed).toBe(true); + let searchAlpha1 = await getPost(alpha, searchBeta1.posts[0].post.id); + expect(searchAlpha1.post_view.post.removed).toBe(true); // Alpha tries to make post on beta, but it fails because of ban let postRes2 = await createPost(alpha, betaCommunity.community.id); diff --git a/api_tests/src/shared.ts b/api_tests/src/shared.ts index 0523712e0..251f3ed96 100644 --- a/api_tests/src/shared.ts +++ b/api_tests/src/shared.ts @@ -58,6 +58,8 @@ import { CommentReportResponse } from "lemmy-js-client/dist/types/CommentReportR import { CreateCommentReport } from "lemmy-js-client/dist/types/CreateCommentReport"; import { ListCommentReportsResponse } from "lemmy-js-client/dist/types/ListCommentReportsResponse"; import { ListCommentReports } from "lemmy-js-client/dist/types/ListCommentReports"; +import { GetPersonDetailsResponse } from "lemmy-js-client/dist/types/GetPersonDetailsResponse"; +import { GetPersonDetails } from "lemmy-js-client/dist/types/GetPersonDetails"; export interface API { client: LemmyHttp; @@ -646,6 +648,16 @@ export async function saveUserSettings( ): Promise { return api.client.saveUserSettings(form); } +export async function getPersonDetails( + api: API, + person_id: number, +): Promise { + let form: GetPersonDetails = { + auth: api.auth, + person_id: person_id, + }; + return api.client.getPersonDetails(form); +} export async function deleteUser(api: API): Promise { let form: DeleteAccount = { diff --git a/crates/api_common/src/build_response.rs b/crates/api_common/src/build_response.rs index a61da7f0b..8a63f7ad4 100644 --- a/crates/api_common/src/build_response.rs +++ b/crates/api_common/src/build_response.rs @@ -82,19 +82,6 @@ pub async fn build_post_response( Ok(PostResponse { post_view }) } -// this is a variation of build_post_response that returns post even if end-user-delted or mod-removed. -// Assumption is that before this function is ever called, the user is already confirmed to be authorized to view post. -// See GitHub Issue #3588 -pub async fn build_post_response_deleted_allowed( - context: &Data, - _community_id: CommunityId, - person_id: PersonId, - post_id: PostId, -) -> Result { - let post_view = PostView::read(&mut context.pool(), post_id, Some(person_id), Some(true)).await?; - Ok(PostResponse { post_view }) -} - // TODO: this function is a mess and should be split up to handle email seperately #[tracing::instrument(skip_all)] pub async fn send_local_notifs( diff --git a/crates/api_crud/src/post/delete.rs b/crates/api_crud/src/post/delete.rs index fa1c03108..eaeb66c43 100644 --- a/crates/api_crud/src/post/delete.rs +++ b/crates/api_crud/src/post/delete.rs @@ -1,7 +1,7 @@ use crate::PerformCrud; use actix_web::web::Data; use lemmy_api_common::{ - build_response::build_post_response_deleted_allowed, + build_response::build_post_response, context::LemmyContext, post::{DeletePost, PostResponse}, utils::{check_community_ban, check_community_deleted_or_removed, local_user_view_from_jwt}, @@ -52,7 +52,7 @@ impl PerformCrud for DeletePost { ) .await?; - build_post_response_deleted_allowed( + build_post_response( context, orig_post.community_id, local_user_view.person.id, diff --git a/crates/api_crud/src/post/remove.rs b/crates/api_crud/src/post/remove.rs index 8feff19fc..7950d5047 100644 --- a/crates/api_crud/src/post/remove.rs +++ b/crates/api_crud/src/post/remove.rs @@ -1,7 +1,7 @@ use crate::PerformCrud; use actix_web::web::Data; use lemmy_api_common::{ - build_response::build_post_response_deleted_allowed, + build_response::build_post_response, context::LemmyContext, post::{PostResponse, RemovePost}, utils::{check_community_ban, is_mod_or_admin, local_user_view_from_jwt}, @@ -61,7 +61,7 @@ impl PerformCrud for RemovePost { }; ModRemovePost::create(&mut context.pool(), &form).await?; - build_post_response_deleted_allowed( + build_post_response( context, orig_post.community_id, local_user_view.person.id, diff --git a/crates/apub/src/api/list_comments.rs b/crates/apub/src/api/list_comments.rs index b64871221..f07ce3dad 100644 --- a/crates/apub/src/api/list_comments.rs +++ b/crates/apub/src/api/list_comments.rs @@ -54,7 +54,6 @@ pub async fn list_comments( let parent_path_cloned = parent_path.clone(); let post_id = data.post_id; - let local_user = local_user_view.map(|l| l.local_user); let comments = CommentQuery { listing_type, sort, @@ -63,7 +62,7 @@ pub async fn list_comments( community_id, parent_path: parent_path_cloned, post_id, - local_user: local_user.as_ref(), + local_user: local_user_view.as_ref(), page, limit, ..Default::default() diff --git a/crates/apub/src/api/list_posts.rs b/crates/apub/src/api/list_posts.rs index c60abc966..2ebd6b766 100644 --- a/crates/apub/src/api/list_posts.rs +++ b/crates/apub/src/api/list_posts.rs @@ -8,7 +8,7 @@ use actix_web::web::{Json, Query}; use lemmy_api_common::{ context::LemmyContext, post::{GetPosts, GetPostsResponse}, - utils::{check_private_instance, is_mod_or_admin_opt, local_user_view_from_jwt_opt}, + utils::{check_private_instance, local_user_view_from_jwt_opt}, }; use lemmy_db_schema::source::{community::Community, local_site::LocalSite}; use lemmy_db_views::post_view::PostQuery; @@ -42,21 +42,14 @@ pub async fn list_posts( community_id, )?); - let is_mod_or_admin = Some( - is_mod_or_admin_opt(&mut context.pool(), local_user_view.as_ref(), community_id) - .await - .is_ok(), - ); - let posts = PostQuery { - local_user: local_user_view.map(|l| l.local_user).as_ref(), + local_user: local_user_view.as_ref(), listing_type, sort, community_id, saved_only, page, limit, - is_mod_or_admin, ..Default::default() } .list(&mut context.pool()) diff --git a/crates/apub/src/api/read_person.rs b/crates/apub/src/api/read_person.rs index 60cea7663..5d3c73c30 100644 --- a/crates/apub/src/api/read_person.rs +++ b/crates/apub/src/api/read_person.rs @@ -4,7 +4,7 @@ use actix_web::web::{Json, Query}; use lemmy_api_common::{ context::LemmyContext, person::{GetPersonDetails, GetPersonDetailsResponse}, - utils::{check_private_instance, is_admin, local_user_view_from_jwt_opt}, + utils::{check_private_instance, local_user_view_from_jwt_opt}, }; use lemmy_db_schema::{ source::{local_site::LocalSite, person::Person}, @@ -26,7 +26,6 @@ pub async fn read_person( let local_user_view = local_user_view_from_jwt_opt(data.auth.as_ref(), &context).await; let local_site = LocalSite::read(&mut context.pool()).await?; - let is_admin = local_user_view.as_ref().map(|luv| is_admin(luv).is_ok()); check_private_instance(&local_user_view, &local_site)?; @@ -53,48 +52,38 @@ pub async fn read_person( let limit = data.limit; let saved_only = data.saved_only; let community_id = data.community_id; - let local_user = local_user_view.map(|l| l.local_user); - let local_user_clone = local_user.clone(); + // 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(false) { + Some(person_details_id) + } else { + None + }; let posts = PostQuery { sort, saved_only, - local_user:local_user.as_ref(), + local_user: local_user_view.as_ref(), community_id, - is_mod_or_admin: is_admin, + is_profile_view: Some(true), page, limit, - creator_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 - if !saved_only.unwrap_or(false) { - Some(person_details_id) - } else { - None - } - , + creator_id, ..Default::default() } .list(&mut context.pool()) .await?; let comments = CommentQuery { - local_user: (local_user_clone.as_ref()), + local_user: (local_user_view.as_ref()), sort: (sort.map(post_to_comment_sort_type)), saved_only: (saved_only), show_deleted_and_removed: (Some(false)), community_id: (community_id), + is_profile_view: Some(true), page: (page), limit: (limit), - creator_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 - if !saved_only.unwrap_or(false) { - Some(person_details_id) - } else { - None - } - ), + creator_id, ..Default::default() } .list(&mut context.pool()) diff --git a/crates/apub/src/api/search.rs b/crates/apub/src/api/search.rs index 2c65f302d..ca84606ff 100644 --- a/crates/apub/src/api/search.rs +++ b/crates/apub/src/api/search.rs @@ -50,7 +50,7 @@ pub async fn search( data.community_id }; let creator_id = data.creator_id; - let local_user = local_user_view.map(|l| l.local_user); + let local_user = local_user_view.as_ref().map(|l| l.local_user.clone()); match search_type { SearchType::Posts => { posts = PostQuery { @@ -58,9 +58,8 @@ pub async fn search( listing_type: (listing_type), community_id: (community_id), creator_id: (creator_id), - local_user: (local_user.as_ref()), + local_user: (local_user_view.as_ref()), search_term: (Some(q)), - is_mod_or_admin: (is_admin), page: (page), limit: (limit), ..Default::default() @@ -75,7 +74,7 @@ pub async fn search( search_term: (Some(q)), community_id: (community_id), creator_id: (creator_id), - local_user: (local_user.as_ref()), + local_user: (local_user_view.as_ref()), page: (page), limit: (limit), ..Default::default() @@ -112,15 +111,15 @@ pub async fn search( let community_or_creator_included = data.community_id.is_some() || data.community_name.is_some() || data.creator_id.is_some(); - let local_user_ = local_user.clone(); + let q = data.q.clone(); + posts = PostQuery { sort: (sort), listing_type: (listing_type), community_id: (community_id), creator_id: (creator_id), - local_user: (local_user_.as_ref()), + local_user: (local_user_view.as_ref()), search_term: (Some(q)), - is_mod_or_admin: (is_admin), page: (page), limit: (limit), ..Default::default() @@ -130,14 +129,13 @@ pub async fn search( let q = data.q.clone(); - let local_user_ = local_user.clone(); comments = CommentQuery { sort: (sort.map(post_to_comment_sort_type)), listing_type: (listing_type), search_term: (Some(q)), community_id: (community_id), creator_id: (creator_id), - local_user: (local_user_.as_ref()), + local_user: (local_user_view.as_ref()), page: (page), limit: (limit), ..Default::default() @@ -186,7 +184,6 @@ pub async fn search( community_id: (community_id), creator_id: (creator_id), url_search: (Some(q)), - is_mod_or_admin: (is_admin), page: (page), limit: (limit), ..Default::default() diff --git a/crates/db_views/src/comment_view.rs b/crates/db_views/src/comment_view.rs index 9a9685c79..889adeeb7 100644 --- a/crates/db_views/src/comment_view.rs +++ b/crates/db_views/src/comment_view.rs @@ -1,4 +1,4 @@ -use crate::structs::CommentView; +use crate::structs::{CommentView, LocalUserView}; use diesel::{ result::Error, BoolExpressionMethods, @@ -30,7 +30,6 @@ use lemmy_db_schema::{ source::{ comment::{Comment, CommentSaved}, community::{Community, CommunityFollower, CommunityPersonBan}, - local_user::LocalUser, person::Person, person_block::PersonBlock, post::Post, @@ -163,9 +162,10 @@ pub struct CommentQuery<'a> { pub post_id: Option, pub parent_path: Option, pub creator_id: Option, - pub local_user: Option<&'a LocalUser>, + pub local_user: Option<&'a LocalUserView>, pub search_term: Option, pub saved_only: Option, + pub is_profile_view: Option, pub show_deleted_and_removed: Option, pub page: Option, pub limit: Option, @@ -177,8 +177,11 @@ impl<'a> CommentQuery<'a> { let conn = &mut get_conn(pool).await?; // The left join below will return None in this case - let person_id_join = self.local_user.map(|l| l.person_id).unwrap_or(PersonId(-1)); - let local_user_id_join = self.local_user.map(|l| l.id).unwrap_or(LocalUserId(-1)); + let person_id_join = self.local_user.map(|l| l.person.id).unwrap_or(PersonId(-1)); + let local_user_id_join = self + .local_user + .map(|l| l.local_user.id) + .unwrap_or(LocalUserId(-1)); let mut query = comment::table .inner_join(person::table) @@ -294,12 +297,24 @@ impl<'a> CommentQuery<'a> { query = query.filter(comment_saved::comment_id.is_not_null()); } - if !self.show_deleted_and_removed.unwrap_or(true) { + let is_profile_view = self.is_profile_view.unwrap_or(false); + let is_creator = self.creator_id == self.local_user.map(|l| l.person.id); + // only show deleted comments to creator + if !is_creator { query = query.filter(comment::deleted.eq(false)); + } + + let is_admin = self.local_user.map(|l| l.person.admin).unwrap_or(false); + // only show removed comments to admin when viewing user profile + if !(is_profile_view && is_admin) { query = query.filter(comment::removed.eq(false)); } - if !self.local_user.map(|l| l.show_bot_accounts).unwrap_or(true) { + if !self + .local_user + .map(|l| l.local_user.show_bot_accounts) + .unwrap_or(true) + { query = query.filter(person::bot_account.eq(false)); }; @@ -385,17 +400,19 @@ mod tests { #![allow(clippy::unwrap_used)] #![allow(clippy::indexing_slicing)] - use crate::comment_view::{ - Comment, - CommentQuery, - CommentSortType, - CommentView, - Community, - DbPool, - LocalUser, - Person, - PersonBlock, - Post, + use crate::{ + comment_view::{ + Comment, + CommentQuery, + CommentSortType, + CommentView, + Community, + DbPool, + Person, + PersonBlock, + Post, + }, + structs::LocalUserView, }; use lemmy_db_schema::{ aggregates::structs::CommentAggregates, @@ -407,7 +424,7 @@ mod tests { community::CommunityInsertForm, instance::Instance, language::Language, - local_user::LocalUserInsertForm, + local_user::{LocalUser, LocalUserInsertForm}, person::PersonInsertForm, person_block::PersonBlockForm, post::PostInsertForm, @@ -424,8 +441,7 @@ mod tests { inserted_comment_1: Comment, inserted_comment_2: Comment, inserted_post: Post, - inserted_person: Person, - inserted_local_user: LocalUser, + local_user_view: LocalUserView, inserted_person_2: Person, inserted_community: Community, } @@ -576,14 +592,18 @@ mod tests { let _inserted_comment_like = CommentLike::like(pool, &comment_like_form).await.unwrap(); + let local_user_view = LocalUserView { + local_user: inserted_local_user.clone(), + person: inserted_person.clone(), + counts: Default::default(), + }; Data { inserted_instance, inserted_comment_0, inserted_comment_1, inserted_comment_2, inserted_post, - inserted_person, - inserted_local_user, + local_user_view, inserted_person_2, inserted_community, } @@ -618,7 +638,7 @@ mod tests { let read_comment_views_with_person = CommentQuery { sort: (Some(CommentSortType::Old)), post_id: (Some(data.inserted_post.id)), - local_user: (Some(&data.inserted_local_user)), + local_user: (Some(&data.local_user_view)), ..Default::default() } .list(pool) @@ -636,7 +656,7 @@ mod tests { let read_comment_from_blocked_person = CommentView::read( pool, data.inserted_comment_1.id, - Some(data.inserted_person.id), + Some(data.local_user_view.person.id), ) .await .unwrap(); @@ -734,7 +754,7 @@ mod tests { // by default, user has all languages enabled and should see all comments // (except from blocked user) let all_languages = CommentQuery { - local_user: (Some(&data.inserted_local_user)), + local_user: (Some(&data.local_user_view)), ..Default::default() } .list(pool) @@ -747,11 +767,11 @@ mod tests { .await .unwrap() .unwrap(); - LocalUserLanguage::update(pool, vec![finnish_id], data.inserted_local_user.id) + LocalUserLanguage::update(pool, vec![finnish_id], data.local_user_view.local_user.id) .await .unwrap(); let finnish_comments = CommentQuery { - local_user: (Some(&data.inserted_local_user)), + local_user: (Some(&data.local_user_view)), ..Default::default() } .list(pool) @@ -768,11 +788,15 @@ mod tests { ); // now show all comments with undetermined language (which is the default value) - LocalUserLanguage::update(pool, vec![UNDETERMINED_ID], data.inserted_local_user.id) - .await - .unwrap(); + LocalUserLanguage::update( + pool, + vec![UNDETERMINED_ID], + data.local_user_view.local_user.id, + ) + .await + .unwrap(); let undetermined_comment = CommentQuery { - local_user: (Some(&data.inserted_local_user)), + local_user: (Some(&data.local_user_view)), ..Default::default() } .list(pool) @@ -784,9 +808,13 @@ mod tests { } async fn cleanup(data: Data, pool: &mut DbPool<'_>) { - CommentLike::remove(pool, data.inserted_person.id, data.inserted_comment_0.id) - .await - .unwrap(); + CommentLike::remove( + pool, + data.local_user_view.person.id, + data.inserted_comment_0.id, + ) + .await + .unwrap(); Comment::delete(pool, data.inserted_comment_0.id) .await .unwrap(); @@ -797,7 +825,9 @@ mod tests { Community::delete(pool, data.inserted_community.id) .await .unwrap(); - Person::delete(pool, data.inserted_person.id).await.unwrap(); + Person::delete(pool, data.local_user_view.person.id) + .await + .unwrap(); Person::delete(pool, data.inserted_person_2.id) .await .unwrap(); @@ -819,7 +849,7 @@ mod tests { comment: Comment { id: data.inserted_comment_0.id, content: "Comment 0".into(), - creator_id: data.inserted_person.id, + creator_id: data.local_user_view.person.id, post_id: data.inserted_post.id, removed: false, deleted: false, @@ -832,12 +862,12 @@ mod tests { language_id: LanguageId(37), }, creator: Person { - id: data.inserted_person.id, + id: data.local_user_view.person.id, name: "timmy".into(), display_name: None, - published: data.inserted_person.published, + published: data.local_user_view.person.published, avatar: None, - actor_id: data.inserted_person.actor_id.clone(), + actor_id: data.local_user_view.person.actor_id.clone(), local: true, banned: false, deleted: false, @@ -846,19 +876,19 @@ mod tests { bio: None, banner: None, updated: None, - inbox_url: data.inserted_person.inbox_url.clone(), + inbox_url: data.local_user_view.person.inbox_url.clone(), shared_inbox_url: None, matrix_user_id: None, ban_expires: None, instance_id: data.inserted_instance.id, - private_key: data.inserted_person.private_key.clone(), - public_key: data.inserted_person.public_key.clone(), - last_refreshed_at: data.inserted_person.last_refreshed_at, + private_key: data.local_user_view.person.private_key.clone(), + public_key: data.local_user_view.person.public_key.clone(), + last_refreshed_at: data.local_user_view.person.last_refreshed_at, }, post: Post { id: data.inserted_post.id, name: data.inserted_post.name.clone(), - creator_id: data.inserted_person.id, + creator_id: data.local_user_view.person.id, url: None, body: None, published: data.inserted_post.published, diff --git a/crates/db_views/src/post_view.rs b/crates/db_views/src/post_view.rs index 48faeca28..30693afb6 100644 --- a/crates/db_views/src/post_view.rs +++ b/crates/db_views/src/post_view.rs @@ -1,4 +1,4 @@ -use crate::structs::PostView; +use crate::structs::{LocalUserView, PostView}; use diesel::{ debug_query, dsl::{now, IntervalDsl}, @@ -34,7 +34,6 @@ use lemmy_db_schema::{ }, source::{ community::{Community, CommunityFollower, CommunityPersonBan}, - local_user::LocalUser, person::Person, person_block::PersonBlock, post::{Post, PostRead, PostSaved}, @@ -146,13 +145,21 @@ impl PostView { .into_boxed(); // Hide deleted and removed for non-admins or mods - // Note: one special use case for this flag variable is when end-user-delete post or mod-removed post. if !is_mod_or_admin.unwrap_or(false) { query = query .filter(community::removed.eq(false)) - .filter(community::deleted.eq(false)) .filter(post::removed.eq(false)) - .filter(post::deleted.eq(false)); + // users can see their own deleted posts + .filter( + community::deleted + .eq(false) + .or(post::creator_id.eq(person_id_join)), + ) + .filter( + post::deleted + .eq(false) + .or(post::creator_id.eq(person_id_join)), + ); } let ( @@ -199,12 +206,11 @@ pub struct PostQuery<'a> { pub sort: Option, pub creator_id: Option, pub community_id: Option, - pub local_user: Option<&'a LocalUser>, + pub local_user: Option<&'a LocalUserView>, pub search_term: Option, pub url_search: Option, pub saved_only: Option, - /// Used to show deleted or removed posts for admins - pub is_mod_or_admin: Option, + pub is_profile_view: Option, pub page: Option, pub limit: Option, } @@ -214,8 +220,11 @@ impl<'a> PostQuery<'a> { let conn = &mut get_conn(pool).await?; // The left join below will return None in this case - let person_id_join = self.local_user.map(|l| l.person_id).unwrap_or(PersonId(-1)); - let local_user_id_join = self.local_user.map(|l| l.id).unwrap_or(LocalUserId(-1)); + let person_id_join = self.local_user.map(|l| l.person.id).unwrap_or(PersonId(-1)); + let local_user_id_join = self + .local_user + .map(|l| l.local_user.id) + .unwrap_or(LocalUserId(-1)); let mut query = post::table .inner_join(person::table) @@ -302,14 +311,21 @@ impl<'a> PostQuery<'a> { )) .into_boxed(); - // Hide deleted and removed for non-admins or mods - // TODO This eventually needs to show posts where you are the creator - if !self.is_mod_or_admin.unwrap_or(false) { + let is_profile_view = self.is_profile_view.unwrap_or(false); + let is_creator = self.creator_id == self.local_user.map(|l| l.person.id); + // only show deleted posts to creator + if is_creator { + query = query + .filter(community::deleted.eq(false)) + .filter(post::deleted.eq(false)); + } + + let is_admin = self.local_user.map(|l| l.person.admin).unwrap_or(false); + // only show removed posts to admin when viewing user profile + if !(is_profile_view && is_admin) { query = query .filter(community::removed.eq(false)) - .filter(community::deleted.eq(false)) - .filter(post::removed.eq(false)) - .filter(post::deleted.eq(false)); + .filter(post::removed.eq(false)); } if self.community_id.is_none() { @@ -359,13 +375,21 @@ impl<'a> PostQuery<'a> { ); } - if !self.local_user.map(|l| l.show_nsfw).unwrap_or(false) { + if !self + .local_user + .map(|l| l.local_user.show_nsfw) + .unwrap_or(false) + { query = query .filter(post::nsfw.eq(false)) .filter(community::nsfw.eq(false)); }; - if !self.local_user.map(|l| l.show_bot_accounts).unwrap_or(true) { + if !self + .local_user + .map(|l| l.local_user.show_bot_accounts) + .unwrap_or(true) + { query = query.filter(person::bot_account.eq(false)); }; @@ -374,7 +398,11 @@ impl<'a> PostQuery<'a> { } // Only hide the read posts, if the saved_only is false. Otherwise ppl with the hide_read // setting wont be able to see saved posts. - else if !self.local_user.map(|l| l.show_read_posts).unwrap_or(true) { + else if !self + .local_user + .map(|l| l.local_user.show_read_posts) + .unwrap_or(true) + { query = query.filter(post_read::post_id.is_null()); } @@ -477,7 +505,10 @@ mod tests { #![allow(clippy::unwrap_used)] #![allow(clippy::indexing_slicing)] - use crate::post_view::{PostQuery, PostView}; + use crate::{ + post_view::{PostQuery, PostView}, + structs::LocalUserView, + }; use lemmy_db_schema::{ aggregates::structs::PostAggregates, impls::actor_language::UNDETERMINED_ID, @@ -502,8 +533,7 @@ mod tests { struct Data { inserted_instance: Instance, - inserted_person: Person, - inserted_local_user: LocalUser, + local_user_view: LocalUserView, inserted_blocked_person: Person, inserted_bot: Person, inserted_community: Community, @@ -592,11 +622,15 @@ mod tests { .build(); let _inserted_bot_post = Post::create(pool, &new_bot_post).await.unwrap(); + let local_user_view = LocalUserView { + local_user: inserted_local_user, + person: inserted_person, + counts: Default::default(), + }; Data { inserted_instance, - inserted_person, - inserted_local_user, + local_user_view, inserted_blocked_person, inserted_bot, inserted_community, @@ -609,20 +643,21 @@ mod tests { async fn post_listing_with_person() { let pool = &build_db_pool_for_tests().await; let pool = &mut pool.into(); - let data = init_data(pool).await; + let mut data = init_data(pool).await; let local_user_form = LocalUserUpdateForm::builder() .show_bot_accounts(Some(false)) .build(); let inserted_local_user = - LocalUser::update(pool, data.inserted_local_user.id, &local_user_form) + LocalUser::update(pool, data.local_user_view.local_user.id, &local_user_form) .await .unwrap(); + data.local_user_view.local_user = inserted_local_user; let read_post_listing = PostQuery { sort: (Some(SortType::New)), community_id: (Some(data.inserted_community.id)), - local_user: (Some(&inserted_local_user)), + local_user: (Some(&data.local_user_view)), ..Default::default() } .list(pool) @@ -632,7 +667,7 @@ mod tests { let post_listing_single_with_person = PostView::read( pool, data.inserted_post.id, - Some(data.inserted_person.id), + Some(data.local_user_view.person.id), None, ) .await @@ -654,14 +689,15 @@ mod tests { .show_bot_accounts(Some(true)) .build(); let inserted_local_user = - LocalUser::update(pool, data.inserted_local_user.id, &local_user_form) + LocalUser::update(pool, data.local_user_view.local_user.id, &local_user_form) .await .unwrap(); + data.local_user_view.local_user = inserted_local_user; let post_listings_with_bots = PostQuery { sort: (Some(SortType::New)), community_id: (Some(data.inserted_community.id)), - local_user: (Some(&inserted_local_user)), + local_user: (Some(&data.local_user_view)), ..Default::default() } .list(pool) @@ -719,7 +755,7 @@ mod tests { let data = init_data(pool).await; let community_block = CommunityBlockForm { - person_id: data.inserted_person.id, + person_id: data.local_user_view.person.id, community_id: data.inserted_community.id, }; CommunityBlock::block(pool, &community_block).await.unwrap(); @@ -727,7 +763,7 @@ mod tests { let read_post_listings_with_person_after_block = PostQuery { sort: (Some(SortType::New)), community_id: (Some(data.inserted_community.id)), - local_user: (Some(&data.inserted_local_user)), + local_user: (Some(&data.local_user_view)), ..Default::default() } .list(pool) @@ -747,11 +783,11 @@ mod tests { async fn post_listing_like() { let pool = &build_db_pool_for_tests().await; let pool = &mut pool.into(); - let data = init_data(pool).await; + let mut data = init_data(pool).await; let post_like_form = PostLikeForm { post_id: data.inserted_post.id, - person_id: data.inserted_person.id, + person_id: data.local_user_view.person.id, score: 1, }; @@ -760,7 +796,7 @@ mod tests { let expected_post_like = PostLike { id: inserted_post_like.id, post_id: data.inserted_post.id, - person_id: data.inserted_person.id, + person_id: data.local_user_view.person.id, published: inserted_post_like.published, score: 1, }; @@ -769,7 +805,7 @@ mod tests { let post_listing_single_with_person = PostView::read( pool, data.inserted_post.id, - Some(data.inserted_person.id), + Some(data.local_user_view.person.id), None, ) .await @@ -785,14 +821,15 @@ mod tests { .show_bot_accounts(Some(false)) .build(); let inserted_local_user = - LocalUser::update(pool, data.inserted_local_user.id, &local_user_form) + LocalUser::update(pool, data.local_user_view.local_user.id, &local_user_form) .await .unwrap(); + data.local_user_view.local_user = inserted_local_user; let read_post_listing = PostQuery { sort: (Some(SortType::New)), community_id: (Some(data.inserted_community.id)), - local_user: (Some(&inserted_local_user)), + local_user: (Some(&data.local_user_view)), ..Default::default() } .list(pool) @@ -802,9 +839,10 @@ mod tests { assert_eq!(expected_post_with_upvote, read_post_listing[0]); - let like_removed = PostLike::remove(pool, data.inserted_person.id, data.inserted_post.id) - .await - .unwrap(); + let like_removed = + PostLike::remove(pool, data.local_user_view.person.id, data.inserted_post.id) + .await + .unwrap(); assert_eq!(1, like_removed); cleanup(data, pool).await; } @@ -822,7 +860,7 @@ mod tests { .unwrap(); let post_spanish = PostInsertForm::builder() .name("asffgdsc".to_string()) - .creator_id(data.inserted_person.id) + .creator_id(data.local_user_view.person.id) .community_id(data.inserted_community.id) .language_id(Some(spanish_id)) .build(); @@ -831,7 +869,7 @@ mod tests { let post_listings_all = PostQuery { sort: (Some(SortType::New)), - local_user: (Some(&data.inserted_local_user)), + local_user: (Some(&data.local_user_view)), ..Default::default() } .list(pool) @@ -845,13 +883,13 @@ mod tests { .await .unwrap() .unwrap(); - LocalUserLanguage::update(pool, vec![french_id], data.inserted_local_user.id) + LocalUserLanguage::update(pool, vec![french_id], data.local_user_view.local_user.id) .await .unwrap(); let post_listing_french = PostQuery { sort: (Some(SortType::New)), - local_user: (Some(&data.inserted_local_user)), + local_user: (Some(&data.local_user_view)), ..Default::default() } .list(pool) @@ -867,13 +905,13 @@ mod tests { LocalUserLanguage::update( pool, vec![french_id, UNDETERMINED_ID], - data.inserted_local_user.id, + data.local_user_view.local_user.id, ) .await .unwrap(); let post_listings_french_und = PostQuery { sort: (Some(SortType::New)), - local_user: (Some(&data.inserted_local_user)), + local_user: (Some(&data.local_user_view)), ..Default::default() } .list(pool) @@ -891,6 +929,49 @@ mod tests { cleanup(data, pool).await; } + #[tokio::test] + #[serial] + async fn post_listings_removed() { + let pool = &build_db_pool_for_tests().await; + let pool = &mut pool.into(); + let mut data = init_data(pool).await; + + // Remove the post + Post::update( + pool, + data.inserted_post.id, + &PostUpdateForm::builder().removed(Some(true)).build(), + ) + .await + .unwrap(); + + // Make sure you don't see the removed post in the results + let post_listings_no_admin = PostQuery { + sort: Some(SortType::New), + local_user: Some(&data.local_user_view), + ..Default::default() + } + .list(pool) + .await + .unwrap(); + assert_eq!(1, post_listings_no_admin.len()); + + // Removed post is shown to admins on profile page + data.local_user_view.person.admin = true; + let post_listings_is_admin = PostQuery { + sort: Some(SortType::New), + local_user: Some(&data.local_user_view), + is_profile_view: Some(true), + ..Default::default() + } + .list(pool) + .await + .unwrap(); + assert_eq!(2, post_listings_is_admin.len()); + + cleanup(data, pool).await; + } + #[tokio::test] #[serial] async fn post_listings_deleted() { @@ -908,30 +989,33 @@ mod tests { .unwrap(); // Make sure you don't see the deleted post in the results - let post_listings_no_admin = PostQuery { - sort: (Some(SortType::New)), - local_user: (Some(&data.inserted_local_user)), - is_mod_or_admin: (Some(false)), + let post_listings_no_creator = PostQuery { + sort: Some(SortType::New), ..Default::default() } .list(pool) .await .unwrap(); + let not_contains_deleted = post_listings_no_creator + .iter() + .map(|p| p.post.id) + .all(|p| p != data.inserted_post.id); + assert!(not_contains_deleted); - assert_eq!(1, post_listings_no_admin.len()); - - // Make sure they see both - let post_listings_is_admin = PostQuery { - sort: (Some(SortType::New)), - local_user: (Some(&data.inserted_local_user)), - is_mod_or_admin: (Some(true)), + // Deleted post is shown to creator + let post_listings_is_creator = PostQuery { + sort: Some(SortType::New), + local_user: Some(&data.local_user_view), ..Default::default() } .list(pool) .await .unwrap(); - - assert_eq!(2, post_listings_is_admin.len()); + let contains_deleted = post_listings_is_creator + .iter() + .map(|p| p.post.id) + .any(|p| p == data.inserted_post.id); + assert!(contains_deleted); cleanup(data, pool).await; } @@ -941,7 +1025,9 @@ mod tests { Community::delete(pool, data.inserted_community.id) .await .unwrap(); - Person::delete(pool, data.inserted_person.id).await.unwrap(); + Person::delete(pool, data.local_user_view.person.id) + .await + .unwrap(); Person::delete(pool, data.inserted_bot.id).await.unwrap(); Person::delete(pool, data.inserted_blocked_person.id) .await @@ -954,7 +1040,7 @@ mod tests { async fn expected_post_view(data: &Data, pool: &mut DbPool<'_>) -> PostView { let (inserted_person, inserted_community, inserted_post) = ( - &data.inserted_person, + &data.local_user_view.person, &data.inserted_community, &data.inserted_post, ); diff --git a/crates/routes/src/feeds.rs b/crates/routes/src/feeds.rs index 49cdb27b5..3abd8eed0 100644 --- a/crates/routes/src/feeds.rs +++ b/crates/routes/src/feeds.rs @@ -13,7 +13,7 @@ use lemmy_db_schema::{ }; use lemmy_db_views::{ post_view::PostQuery, - structs::{PostView, SiteView}, + structs::{LocalUserView, PostView, SiteView}, }; use lemmy_db_views_actor::{ comment_reply_view::CommentReplyQuery, @@ -326,7 +326,7 @@ async fn get_feed_front( ) -> Result { let site_view = SiteView::read_local(pool).await?; let local_user_id = LocalUserId(Claims::decode(jwt, jwt_secret)?.claims.sub); - let local_user = LocalUser::read(pool, local_user_id).await?; + let local_user = LocalUserView::read(pool, local_user_id).await?; let posts = PostQuery { listing_type: (Some(ListingType::Subscribed)), From ced3aa5bd8bc78fb7a1a98bd839531255b773a9c Mon Sep 17 00:00:00 2001 From: Dessalines Date: Thu, 20 Jul 2023 10:44:23 -0400 Subject: [PATCH 2/3] Fixing hot_ranks and scores to append a published sort. (#3618) - #3428 --- crates/db_views/src/comment_view.rs | 8 +++-- crates/db_views/src/post_view.rs | 8 +++-- .../down.sql | 26 ++++++++++++++++ .../up.sql | 30 +++++++++++++++++++ 4 files changed, 68 insertions(+), 4 deletions(-) create mode 100644 migrations/2023-07-14-154840_add_optimized_indexes_published/down.sql create mode 100644 migrations/2023-07-14-154840_add_optimized_indexes_published/up.sql diff --git a/crates/db_views/src/comment_view.rs b/crates/db_views/src/comment_view.rs index 889adeeb7..e98d9f879 100644 --- a/crates/db_views/src/comment_view.rs +++ b/crates/db_views/src/comment_view.rs @@ -360,10 +360,14 @@ impl<'a> CommentQuery<'a> { }; query = match self.sort.unwrap_or(CommentSortType::Hot) { - CommentSortType::Hot => query.then_order_by(comment_aggregates::hot_rank.desc()), + CommentSortType::Hot => query + .then_order_by(comment_aggregates::hot_rank.desc()) + .then_order_by(comment_aggregates::published.desc()), + CommentSortType::Top => query + .order_by(comment_aggregates::score.desc()) + .then_order_by(comment_aggregates::published.desc()), CommentSortType::New => query.then_order_by(comment::published.desc()), CommentSortType::Old => query.then_order_by(comment::published.asc()), - CommentSortType::Top => query.order_by(comment_aggregates::score.desc()), }; // Note: deleted and removed comments are done on the front side diff --git a/crates/db_views/src/post_view.rs b/crates/db_views/src/post_view.rs index 30693afb6..1d85df3c8 100644 --- a/crates/db_views/src/post_view.rs +++ b/crates/db_views/src/post_view.rs @@ -416,8 +416,12 @@ impl<'a> PostQuery<'a> { } query = match self.sort.unwrap_or(SortType::Hot) { - SortType::Active => query.then_order_by(post_aggregates::hot_rank_active.desc()), - SortType::Hot => query.then_order_by(post_aggregates::hot_rank.desc()), + SortType::Active => query + .then_order_by(post_aggregates::hot_rank_active.desc()) + .then_order_by(post_aggregates::published.desc()), + SortType::Hot => query + .then_order_by(post_aggregates::hot_rank.desc()) + .then_order_by(post_aggregates::published.desc()), SortType::New => query.then_order_by(post_aggregates::published.desc()), SortType::Old => query.then_order_by(post_aggregates::published.asc()), SortType::NewComments => query.then_order_by(post_aggregates::newest_comment_time.desc()), diff --git a/migrations/2023-07-14-154840_add_optimized_indexes_published/down.sql b/migrations/2023-07-14-154840_add_optimized_indexes_published/down.sql new file mode 100644 index 000000000..5661a3146 --- /dev/null +++ b/migrations/2023-07-14-154840_add_optimized_indexes_published/down.sql @@ -0,0 +1,26 @@ +-- Drop the new indexes +drop index idx_post_aggregates_featured_local_most_comments; +drop index idx_post_aggregates_featured_local_hot; +drop index idx_post_aggregates_featured_local_active; +drop index idx_post_aggregates_featured_local_score; +drop index idx_post_aggregates_featured_community_hot; +drop index idx_post_aggregates_featured_community_active; +drop index idx_post_aggregates_featured_community_score; +drop index idx_post_aggregates_featured_community_most_comments; +drop index idx_comment_aggregates_hot; +drop index idx_comment_aggregates_score; + +-- Add the old ones back in +-- featured_local +create index idx_post_aggregates_featured_local_hot on post_aggregates (featured_local desc, hot_rank desc); +create index idx_post_aggregates_featured_local_active on post_aggregates (featured_local desc, hot_rank_active desc); +create index idx_post_aggregates_featured_local_score on post_aggregates (featured_local desc, score desc); + +-- featured_community +create index idx_post_aggregates_featured_community_hot on post_aggregates (featured_community desc, hot_rank desc); +create index idx_post_aggregates_featured_community_active on post_aggregates (featured_community desc, hot_rank_active desc); +create index idx_post_aggregates_featured_community_score on post_aggregates (featured_community desc, score desc); + +create index idx_comment_aggregates_hot on comment_aggregates (hot_rank desc); +create index idx_comment_aggregates_score on comment_aggregates (score desc); + diff --git a/migrations/2023-07-14-154840_add_optimized_indexes_published/up.sql b/migrations/2023-07-14-154840_add_optimized_indexes_published/up.sql new file mode 100644 index 000000000..94e426fc7 --- /dev/null +++ b/migrations/2023-07-14-154840_add_optimized_indexes_published/up.sql @@ -0,0 +1,30 @@ +-- Drop the old indexes +drop index idx_post_aggregates_featured_local_hot; +drop index idx_post_aggregates_featured_local_active; +drop index idx_post_aggregates_featured_local_score; +drop index idx_post_aggregates_featured_community_hot; +drop index idx_post_aggregates_featured_community_active; +drop index idx_post_aggregates_featured_community_score; +drop index idx_comment_aggregates_hot; +drop index idx_comment_aggregates_score; + +-- Add a published desc, to the end of the hot and active ranks + +-- Add missing most comments index +create index idx_post_aggregates_featured_local_most_comments on post_aggregates (featured_local desc, comments desc, published desc); +create index idx_post_aggregates_featured_community_most_comments on post_aggregates (featured_community desc, comments desc, published desc); + +-- featured_local +create index idx_post_aggregates_featured_local_hot on post_aggregates (featured_local desc, hot_rank desc, published desc); +create index idx_post_aggregates_featured_local_active on post_aggregates (featured_local desc, hot_rank_active desc, published desc); +create index idx_post_aggregates_featured_local_score on post_aggregates (featured_local desc, score desc, published desc); + +-- featured_community +create index idx_post_aggregates_featured_community_hot on post_aggregates (featured_community desc, hot_rank desc, published desc); +create index idx_post_aggregates_featured_community_active on post_aggregates (featured_community desc, hot_rank_active desc, published desc); +create index idx_post_aggregates_featured_community_score on post_aggregates (featured_community desc, score desc, published desc); + +-- Fixing some comment aggregates ones +create index idx_comment_aggregates_hot on comment_aggregates (hot_rank desc, published desc); +create index idx_comment_aggregates_score on comment_aggregates (score desc, published desc); + From b511c2e6cb27b432accddc4e9aa2ec3a4bb7861b Mon Sep 17 00:00:00 2001 From: Sander Saarend Date: Thu, 20 Jul 2023 18:13:21 +0300 Subject: [PATCH 3/3] Denormalize community_id into post_aggregates for a 1000x speed-up when loading posts (#3653) * Denormalize community_id into post_aggregates for a 1000x speed-up when loading posts * Remove unused index * Add creator_id to post_aggregates * Use post_aggregates as main table for PostQuery * Make post_aggregates the main table for PostView * Reformat SQL --- crates/db_schema/src/aggregates/structs.rs | 2 + crates/db_schema/src/schema.rs | 4 ++ crates/db_views/src/post_report_view.rs | 2 + crates/db_views/src/post_view.rs | 50 ++++++++++--------- .../down.sql | 20 ++++++++ .../up.sql | 35 +++++++++++++ 6 files changed, 89 insertions(+), 24 deletions(-) create mode 100644 migrations/2023-07-18-082614_post_aggregates_community_id/down.sql create mode 100644 migrations/2023-07-18-082614_post_aggregates_community_id/up.sql diff --git a/crates/db_schema/src/aggregates/structs.rs b/crates/db_schema/src/aggregates/structs.rs index 592c63abb..1af94a800 100644 --- a/crates/db_schema/src/aggregates/structs.rs +++ b/crates/db_schema/src/aggregates/structs.rs @@ -96,6 +96,8 @@ pub struct PostAggregates { pub featured_local: bool, pub hot_rank: i32, pub hot_rank_active: i32, + pub community_id: CommunityId, + pub creator_id: PersonId, } #[derive(PartialEq, Eq, Debug, Serialize, Deserialize, Clone)] diff --git a/crates/db_schema/src/schema.rs b/crates/db_schema/src/schema.rs index ae75c31d8..17a0f99f8 100644 --- a/crates/db_schema/src/schema.rs +++ b/crates/db_schema/src/schema.rs @@ -672,6 +672,8 @@ diesel::table! { featured_local -> Bool, hot_rank -> Int4, hot_rank_active -> Int4, + community_id -> Int4, + creator_id -> Int4, } } @@ -908,6 +910,8 @@ diesel::joinable!(person_post_aggregates -> post (post_id)); diesel::joinable!(post -> community (community_id)); diesel::joinable!(post -> language (language_id)); diesel::joinable!(post -> person (creator_id)); +diesel::joinable!(post_aggregates -> community (community_id)); +diesel::joinable!(post_aggregates -> person (creator_id)); diesel::joinable!(post_aggregates -> post (post_id)); diesel::joinable!(post_like -> person (person_id)); diesel::joinable!(post_like -> post (post_id)); diff --git a/crates/db_views/src/post_report_view.rs b/crates/db_views/src/post_report_view.rs index 50b35b1c2..a53762e21 100644 --- a/crates/db_views/src/post_report_view.rs +++ b/crates/db_views/src/post_report_view.rs @@ -470,6 +470,8 @@ mod tests { featured_local: false, hot_rank: 1728, hot_rank_active: 1728, + community_id: inserted_post.community_id, + creator_id: inserted_post.creator_id, }, resolver: None, }; diff --git a/crates/db_views/src/post_view.rs b/crates/db_views/src/post_view.rs index 1d85df3c8..d2f6ab759 100644 --- a/crates/db_views/src/post_view.rs +++ b/crates/db_views/src/post_view.rs @@ -72,56 +72,56 @@ impl PostView { // The left join below will return None in this case let person_id_join = my_person_id.unwrap_or(PersonId(-1)); - let mut query = post::table - .find(post_id) + let mut query = post_aggregates::table + .filter(post_aggregates::post_id.eq(post_id)) .inner_join(person::table) .inner_join(community::table) .left_join( community_person_ban::table.on( - post::community_id + post_aggregates::community_id .eq(community_person_ban::community_id) - .and(community_person_ban::person_id.eq(post::creator_id)), + .and(community_person_ban::person_id.eq(post_aggregates::creator_id)), ), ) - .inner_join(post_aggregates::table) + .inner_join(post::table) .left_join( community_follower::table.on( - post::community_id + post_aggregates::community_id .eq(community_follower::community_id) .and(community_follower::person_id.eq(person_id_join)), ), ) .left_join( post_saved::table.on( - post::id + post_aggregates::post_id .eq(post_saved::post_id) .and(post_saved::person_id.eq(person_id_join)), ), ) .left_join( post_read::table.on( - post::id + post_aggregates::post_id .eq(post_read::post_id) .and(post_read::person_id.eq(person_id_join)), ), ) .left_join( person_block::table.on( - post::creator_id + post_aggregates::creator_id .eq(person_block::target_id) .and(person_block::person_id.eq(person_id_join)), ), ) .left_join( post_like::table.on( - post::id + post_aggregates::post_id .eq(post_like::post_id) .and(post_like::person_id.eq(person_id_join)), ), ) .left_join( person_post_aggregates::table.on( - post::id + post_aggregates::post_id .eq(person_post_aggregates::post_id) .and(person_post_aggregates::person_id.eq(person_id_join)), ), @@ -226,62 +226,62 @@ impl<'a> PostQuery<'a> { .map(|l| l.local_user.id) .unwrap_or(LocalUserId(-1)); - let mut query = post::table + let mut query = post_aggregates::table .inner_join(person::table) + .inner_join(post::table) .inner_join(community::table) .left_join( community_person_ban::table.on( - post::community_id + post_aggregates::community_id .eq(community_person_ban::community_id) - .and(community_person_ban::person_id.eq(post::creator_id)), + .and(community_person_ban::person_id.eq(post_aggregates::creator_id)), ), ) - .inner_join(post_aggregates::table) .left_join( community_follower::table.on( - post::community_id + post_aggregates::community_id .eq(community_follower::community_id) .and(community_follower::person_id.eq(person_id_join)), ), ) .left_join( post_saved::table.on( - post::id + post_aggregates::post_id .eq(post_saved::post_id) .and(post_saved::person_id.eq(person_id_join)), ), ) .left_join( post_read::table.on( - post::id + post_aggregates::post_id .eq(post_read::post_id) .and(post_read::person_id.eq(person_id_join)), ), ) .left_join( person_block::table.on( - post::creator_id + post_aggregates::creator_id .eq(person_block::target_id) .and(person_block::person_id.eq(person_id_join)), ), ) .left_join( community_block::table.on( - post::community_id + post_aggregates::community_id .eq(community_block::community_id) .and(community_block::person_id.eq(person_id_join)), ), ) .left_join( post_like::table.on( - post::id + post_aggregates::post_id .eq(post_like::post_id) .and(post_like::person_id.eq(person_id_join)), ), ) .left_join( person_post_aggregates::table.on( - post::id + post_aggregates::post_id .eq(person_post_aggregates::post_id) .and(person_post_aggregates::person_id.eq(person_id_join)), ), @@ -332,12 +332,12 @@ impl<'a> PostQuery<'a> { query = query.then_order_by(post_aggregates::featured_local.desc()); } else if let Some(community_id) = self.community_id { query = query - .filter(post::community_id.eq(community_id)) + .filter(post_aggregates::community_id.eq(community_id)) .then_order_by(post_aggregates::featured_community.desc()); } if let Some(creator_id) = self.creator_id { - query = query.filter(post::creator_id.eq(creator_id)); + query = query.filter(post_aggregates::creator_id.eq(creator_id)); } if let Some(listing_type) = self.listing_type { @@ -1141,6 +1141,8 @@ mod tests { featured_local: false, hot_rank: 1728, hot_rank_active: 1728, + community_id: inserted_post.community_id, + creator_id: inserted_post.creator_id, }, subscribed: SubscribedType::NotSubscribed, read: false, diff --git a/migrations/2023-07-18-082614_post_aggregates_community_id/down.sql b/migrations/2023-07-18-082614_post_aggregates_community_id/down.sql new file mode 100644 index 000000000..91e2dc862 --- /dev/null +++ b/migrations/2023-07-18-082614_post_aggregates_community_id/down.sql @@ -0,0 +1,20 @@ +-- This file should undo anything in `up.sql` + +CREATE OR REPLACE FUNCTION post_aggregates_post() + RETURNS trigger + LANGUAGE plpgsql +AS +$$ +BEGIN + IF (TG_OP = 'INSERT') THEN + INSERT INTO post_aggregates (post_id, published, newest_comment_time, newest_comment_time_necro) + VALUES (NEW.id, NEW.published, NEW.published, NEW.published); + ELSIF (TG_OP = 'DELETE') THEN + DELETE FROM post_aggregates WHERE post_id = OLD.id; + END IF; + RETURN NULL; +END +$$; + +ALTER TABLE post_aggregates DROP COLUMN community_id, DROP COLUMN creator_id; + diff --git a/migrations/2023-07-18-082614_post_aggregates_community_id/up.sql b/migrations/2023-07-18-082614_post_aggregates_community_id/up.sql new file mode 100644 index 000000000..f28701da0 --- /dev/null +++ b/migrations/2023-07-18-082614_post_aggregates_community_id/up.sql @@ -0,0 +1,35 @@ +-- Your SQL goes here +ALTER TABLE post_aggregates + ADD COLUMN community_id integer REFERENCES community ON UPDATE CASCADE ON DELETE CASCADE, + ADD COLUMN creator_id integer REFERENCES person ON UPDATE CASCADE ON DELETE CASCADE; + +CREATE OR REPLACE FUNCTION post_aggregates_post() + RETURNS trigger + LANGUAGE plpgsql +AS +$$ +BEGIN + IF (TG_OP = 'INSERT') THEN + INSERT INTO post_aggregates (post_id, + published, + newest_comment_time, + newest_comment_time_necro, + community_id, + creator_id) + VALUES (NEW.id, NEW.published, NEW.published, NEW.published, NEW.community_id, NEW.creator_id); + ELSIF (TG_OP = 'DELETE') THEN + DELETE FROM post_aggregates WHERE post_id = OLD.id; + END IF; + RETURN NULL; +END +$$; + +UPDATE post_aggregates +SET community_id=post.community_id, + creator_id=post.creator_id +FROM post +WHERE post.id = post_aggregates.post_id; + +ALTER TABLE post_aggregates + ALTER COLUMN community_id SET NOT NULL, + ALTER COLUMN creator_id SET NOT NULL; \ No newline at end of file