From 64c75868e6186c0fa4b85aed07a3b14adea53ba5 Mon Sep 17 00:00:00 2001 From: dullbananas Date: Thu, 16 Oct 2025 12:22:34 -0700 Subject: [PATCH] Shrink things that are rewritten in slow migrations (#5874) * update schema.rs * Require indexes for all foreign keys (#5872) * add test * add migration with missing indexes * Fix foreign key indexes migration (#5875) * Allow remote groups to follow Lemmy communities (fixes #5354) (#5864) * Allow remote groups to follow Lemmy communities (fixes #5354) * index * rename columns * review changes * primary key * Prevent stack overflow when fetching nested comment (#5787) * attempt future wrapper * Revert "attempt future wrapper" This reverts commit ce95422228ce91ed09eb34601c20d183ac31f93d. * use spawn * remove `lazy` and change comment * temporary change for test * change 5000 back to 50 * fix comment about async laziness * make columns nullable * more shrinking * update utils.sql * update remove-aggregate-tables migration, and change indexed ranks back to non-null * rename columns in remove-aggregate-tables migration, and fix later migrations * remove comment * optimize rank functions * stuff * more migration fixes * other changes to migrations * make schema setup tests pass * stuff * make tests pass * crud trait is back * add todo * change immutable to stable * use minutes for age * clean up * start cursor refactor * finish cursor refactor * remove "get_" prefix * clean up * import style consistency * revert change to translations commit * use i-love-jesus from crates.io * remove some non_0_ and non_1_ * revert most changes in the crates folder * fix column order in structs * stuff * fix migrations * stuff (rust tests now pass) * rename some vote-related fields * fix some rust errors to make ts bindings generation work, and rename voteview field * lint * change stuff in api_tests to match changes to types * change translations commit to prevent merge conflict * rename "is_positive" * remove like score range check test from post.spec.ts * uncomment restrict-key thing * prettier * rerun ci * add cfg_attr * fix "A required parameter cannot follow an optional parameter" * person_liked_combined: RENAME COLUMN liked_at TO voted_at * for "newest comment" times: remove "after_published" prefix and don't convert to non-null * move "voted_at" rename to "rename_timestamp_add_at" migration * remove added uses of as_select * fix sql code referencing "liked_at" * clippy * restore as_select uses that were already present * change lemmy-js-client version --------- Co-authored-by: Nutomic Co-authored-by: Dessalines Co-authored-by: Dessalines --- api_tests/package.json | 2 +- api_tests/pnpm-lock.yaml | 10 +- api_tests/src/comment.spec.ts | 8 +- api_tests/src/post.spec.ts | 38 +- api_tests/src/private_comm.spec.ts | 4 +- api_tests/src/shared.ts | 8 +- crates/api/api/src/comment/like.rs | 19 +- crates/api/api/src/post/like.rs | 19 +- crates/api/api/src/site/mod_log.rs | 23 +- crates/api/api_crud/src/comment/create.rs | 2 +- crates/api/api_crud/src/post/create.rs | 2 +- crates/api/api_utils/src/send_activity.rs | 4 +- crates/api/api_utils/src/utils.rs | 6 +- .../src/create_or_update/comment.rs | 2 +- .../activities/src/create_or_update/post.rs | 2 +- crates/apub/activities/src/lib.rs | 8 +- .../activities/src/protocol/voting/vote.rs | 25 +- crates/apub/activities/src/voting/mod.rs | 12 +- crates/db_schema/src/impls/comment.rs | 26 +- crates/db_schema/src/impls/person.rs | 24 +- crates/db_schema/src/impls/post.rs | 39 +- crates/db_schema/src/lib.rs | 2 +- .../src/source/combined/person_liked.rs | 6 +- .../db_schema/src/source/combined/search.rs | 32 -- crates/db_schema/src/source/comment.rs | 20 +- crates/db_schema/src/source/community.rs | 24 +- crates/db_schema/src/source/instance.rs | 4 +- crates/db_schema/src/source/person.rs | 8 +- crates/db_schema/src/source/post.rs | 46 +-- crates/db_schema/src/traits.rs | 17 +- crates/db_schema/src/utils.rs | 40 ++- crates/db_schema/src/utils/queries/selects.rs | 4 +- crates/db_schema_file/src/schema.rs | 56 +-- .../replaceable_schema/triggers.sql | 21 +- .../replaceable_schema/utils.sql | 22 +- crates/db_views/comment/src/api.rs | 3 +- crates/db_views/comment/src/impls.rs | 4 +- .../person_liked_combined/src/impls.rs | 30 +- crates/db_views/post/src/api.rs | 3 +- crates/db_views/post/src/impls.rs | 33 +- crates/db_views/search_combined/src/impls.rs | 22 +- crates/db_views/vote/src/impls.rs | 33 +- crates/db_views/vote/src/lib.rs | 2 +- crates/routes/src/utils/scheduled_tasks.rs | 7 +- .../down.sql | 12 +- .../up.sql | 50 +-- .../down.sql | 13 +- .../up.sql | 123 +++++-- .../down.sql | 129 +------ .../down.sql | 32 +- .../down.sql | 339 ------------------ .../up.sql | 3 - .../up.sql | 8 +- .../up.sql | 25 +- .../down.sql | 6 +- .../up.sql | 6 +- .../2025-08-01-000059_person_votes/up.sql | 8 +- .../down.sql | 2 +- .../up.sql | 8 +- 59 files changed, 514 insertions(+), 972 deletions(-) delete mode 100644 migrations/2025-08-01-000046_remove_post_instance_id/down.sql delete mode 100644 migrations/2025-08-01-000046_remove_post_instance_id/up.sql diff --git a/api_tests/package.json b/api_tests/package.json index 235cb892e..38bb9e948 100644 --- a/api_tests/package.json +++ b/api_tests/package.json @@ -31,7 +31,7 @@ "eslint-plugin-prettier": "^5.5.0", "jest": "^30.0.0", "joi": "^17.13.3", - "lemmy-js-client": "1.0.0-enum-variants-snake-case.3", + "lemmy-js-client": "1.0.0-bool-vote.2", "prettier": "^3.5.3", "ts-jest": "^29.4.0", "tsoa": "^6.6.0", diff --git a/api_tests/pnpm-lock.yaml b/api_tests/pnpm-lock.yaml index 94f2c15b7..86fc718dc 100644 --- a/api_tests/pnpm-lock.yaml +++ b/api_tests/pnpm-lock.yaml @@ -36,8 +36,8 @@ importers: specifier: ^17.13.3 version: 17.13.3 lemmy-js-client: - specifier: 1.0.0-enum-variants-snake-case.3 - version: 1.0.0-enum-variants-snake-case.3 + specifier: 1.0.0-bool-vote.2 + version: 1.0.0-bool-vote.2 prettier: specifier: ^3.5.3 version: 3.6.2 @@ -1656,8 +1656,8 @@ packages: keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} - lemmy-js-client@1.0.0-enum-variants-snake-case.3: - resolution: {integrity: sha512-IFdDO9KZ7JKfuVJVpb9xqCVZEmZZVnAATFxDiG94RMp0dPAu8wOyD9OcT/nVn7sXmjbuex+3BFwjC2M6TlWj9A==} + lemmy-js-client@1.0.0-bool-vote.2: + resolution: {integrity: sha512-RqePoajuK7WNv63W6FzH4cf+Q5ahNCo6kxriFLS21BsJok9olap4AitgyN6p+swLVFkjIeSj11dCT1Bf8wqt3Q==} leven@3.1.0: resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} @@ -4399,7 +4399,7 @@ snapshots: dependencies: json-buffer: 3.0.1 - lemmy-js-client@1.0.0-enum-variants-snake-case.3: + lemmy-js-client@1.0.0-bool-vote.2: dependencies: '@tsoa/runtime': 6.6.0 transitivePeerDependencies: diff --git a/api_tests/src/comment.spec.ts b/api_tests/src/comment.spec.ts index 638884ae9..01f63b0b5 100644 --- a/api_tests/src/comment.spec.ts +++ b/api_tests/src/comment.spec.ts @@ -292,7 +292,11 @@ test("Unlike a comment", async () => { expect(gammaComment1?.creator.local).toBe(false); expect(gammaComment1?.comment.score).toBe(1); - let unlike = await likeComment(alpha, 0, commentRes.comment_view.comment); + let unlike = await likeComment( + alpha, + undefined, + commentRes.comment_view.comment, + ); expect(unlike.comment_view.comment.score).toBe(0); // Make sure that comment is unliked on beta @@ -330,7 +334,7 @@ test("Federated comment like", async () => { throw "Missing beta comment"; } - let like = await likeComment(beta, 1, betaComment.comment); + let like = await likeComment(beta, true, betaComment.comment); expect(like.comment_view.comment.score).toBe(2); // Get the post from alpha, check the likes diff --git a/api_tests/src/post.spec.ts b/api_tests/src/post.spec.ts index 10f756901..b22e0c2fb 100644 --- a/api_tests/src/post.spec.ts +++ b/api_tests/src/post.spec.ts @@ -169,11 +169,11 @@ test("Unlike a post", async () => { throw "Missing beta community"; } let postRes = await createPost(alpha, betaCommunity.community.id); - let unlike = await likePost(alpha, 0, postRes.post_view.post); + let unlike = await likePost(alpha, undefined, postRes.post_view.post); expect(unlike.post_view.post.score).toBe(0); // Try to unlike it again, make sure it stays at 0 - let unlike2 = await likePost(alpha, 0, postRes.post_view.post); + let unlike2 = await likePost(alpha, undefined, postRes.post_view.post); expect(unlike2.post_view.post.score).toBe(0); // Make sure that post is unliked on beta @@ -190,40 +190,6 @@ test("Unlike a post", async () => { await assertPostFederation(betaPost, postRes.post_view); }); -test("Make sure like is within range", async () => { - if (!betaCommunity) { - throw "Missing beta community"; - } - let postRes = await createPost(alpha, betaCommunity.community.id); - - // Try a like with score 2 - await expect( - likePost(alpha, 2, postRes.post_view.post), - ).rejects.toStrictEqual( - new LemmyError("couldnt_like_post", statusBadRequest), - ); - - // Try a like with score -2 - await expect( - likePost(alpha, -2, postRes.post_view.post), - ).rejects.toStrictEqual( - new LemmyError("couldnt_like_post", statusBadRequest), - ); - - // Make sure that post stayed at 1 - const betaPost = await waitForPost( - beta, - postRes.post_view.post, - post => post?.post.score === 1, - ); - - expect(betaPost).toBeDefined(); - expect(betaPost?.community.local).toBe(true); - expect(betaPost?.creator.local).toBe(false); - expect(betaPost?.post.score).toBe(1); - await assertPostFederation(betaPost, postRes.post_view); -}); - test("Update a post", async () => { if (!betaCommunity) { throw "Missing beta community"; diff --git a/api_tests/src/private_comm.spec.ts b/api_tests/src/private_comm.spec.ts index 1355defd0..5c8f9a815 100644 --- a/api_tests/src/private_comm.spec.ts +++ b/api_tests/src/private_comm.spec.ts @@ -164,8 +164,8 @@ test("Only followers can view and interact with private community content", asyn const post1 = await createPost(user, betaCommunity!.id); expect(post1.post_view).toBeDefined(); - const like = await likeComment(user, 1, resolvedComment!.comment); - expect(like.comment_view.comment_actions?.like_score).toBe(1); + const like = await likeComment(user, true, resolvedComment!.comment); + expect(like.comment_view.comment_actions?.vote_is_upvote).toBe(true); }); test("Reject follower", async () => { diff --git a/api_tests/src/shared.ts b/api_tests/src/shared.ts index 3c2145231..262e9b4c7 100644 --- a/api_tests/src/shared.ts +++ b/api_tests/src/shared.ts @@ -524,12 +524,12 @@ export async function followCommunity( export async function likePost( api: LemmyHttp, - score: number, + is_upvote: boolean | undefined, post: Post, ): Promise { let form: CreatePostLike = { post_id: post.id, - score: score, + is_upvote: is_upvote, }; return api.likePost(form); @@ -588,12 +588,12 @@ export async function removeComment( export async function likeComment( api: LemmyHttp, - score: number, + is_upvote: boolean | undefined, comment: Comment, ): Promise { let form: CreateCommentLike = { comment_id: comment.id, - score, + is_upvote, }; return api.likeComment(form); } diff --git a/crates/api/api/src/comment/like.rs b/crates/api/api/src/comment/like.rs index cc04bf26f..ac11a81c2 100644 --- a/crates/api/api/src/comment/like.rs +++ b/crates/api/api/src/comment/like.rs @@ -41,7 +41,7 @@ pub async fn like_comment( let my_person_id = local_user_view.person.id; check_local_vote_mode( - data.score, + data.is_upvote, PostOrCommentId::Comment(comment_id), &local_site, my_person_id, @@ -57,7 +57,7 @@ pub async fn like_comment( local_instance_id, ) .await?; - let previous_score = orig_comment.comment_actions.and_then(|p| p.like_score); + let previous_is_upvote = orig_comment.comment_actions.and_then(|p| p.vote_is_upvote); check_community_user_action( &local_user_view, @@ -66,30 +66,29 @@ pub async fn like_comment( ) .await?; - let mut like_form = CommentLikeForm::new(my_person_id, data.comment_id, data.score); - // Remove any likes first CommentActions::remove_like(&mut context.pool(), my_person_id, comment_id).await?; - if let Some(previous_score) = previous_score { + if let Some(previous_is_upvote) = previous_is_upvote { PersonActions::remove_like( &mut context.pool(), my_person_id, orig_comment.creator.id, - previous_score, + previous_is_upvote, ) .await // Ignore errors, since a previous_like of zero throws an error .ok(); } - if like_form.like_score != 0 { + if let Some(is_upvote) = data.is_upvote { + let mut like_form = CommentLikeForm::new(my_person_id, data.comment_id, is_upvote); like_form = plugin_hook_before("before_comment_vote", like_form).await?; let like = CommentActions::like(&mut context.pool(), &like_form).await?; PersonActions::like( &mut context.pool(), my_person_id, orig_comment.creator.id, - like_form.like_score, + like_form.vote_is_upvote, ) .await?; @@ -101,8 +100,8 @@ pub async fn like_comment( object_id: orig_comment.comment.ap_id, actor: local_user_view.person.clone(), community: orig_comment.community, - previous_score, - new_score: data.score, + previous_is_upvote, + new_is_upvote: data.is_upvote, }, &context, )?; diff --git a/crates/api/api/src/post/like.rs b/crates/api/api/src/post/like.rs index d21d18914..8267fc936 100644 --- a/crates/api/api/src/post/like.rs +++ b/crates/api/api/src/post/like.rs @@ -41,7 +41,7 @@ pub async fn like_post( let my_person_id = local_user_view.person.id; check_local_vote_mode( - data.score, + data.is_upvote, PostOrCommentId::Post(post_id), &local_site, my_person_id, @@ -59,34 +59,33 @@ pub async fn like_post( false, ) .await?; - let previous_score = orig_post.post_actions.and_then(|p| p.like_score); + let previous_is_upvote = orig_post.post_actions.and_then(|p| p.vote_is_upvote); check_community_user_action(&local_user_view, &orig_post.community, &mut context.pool()).await?; - let mut like_form = PostLikeForm::new(data.post_id, my_person_id, data.score); - // Remove any likes first PostActions::remove_like(&mut context.pool(), my_person_id, post_id).await?; - if let Some(previous_score) = previous_score { + if let Some(previous_is_upvote) = previous_is_upvote { PersonActions::remove_like( &mut context.pool(), my_person_id, orig_post.creator.id, - previous_score, + previous_is_upvote, ) .await // Ignore errors, since a previous_like of zero throws an error .ok(); } - if like_form.like_score != 0 { + if let Some(is_upvote) = data.is_upvote { + let mut like_form = PostLikeForm::new(data.post_id, my_person_id, is_upvote); like_form = plugin_hook_before("before_post_vote", like_form).await?; let like = PostActions::like(&mut context.pool(), &like_form).await?; PersonActions::like( &mut context.pool(), my_person_id, orig_post.creator.id, - like_form.like_score, + like_form.vote_is_upvote, ) .await?; @@ -102,8 +101,8 @@ pub async fn like_post( object_id: orig_post.post.ap_id, actor: local_user_view.person.clone(), community: orig_post.community.clone(), - previous_score, - new_score: data.score, + previous_is_upvote, + new_is_upvote: data.is_upvote, }, &context, )?; diff --git a/crates/api/api/src/site/mod_log.rs b/crates/api/api/src/site/mod_log.rs index 7bca4860b..3436d4318 100644 --- a/crates/api/api/src/site/mod_log.rs +++ b/crates/api/api/src/site/mod_log.rs @@ -138,7 +138,7 @@ mod tests { let post_form_1 = PostInsertForm::new("A test post tubular".into(), sara.id, community.id); let post_1 = Post::create(pool, &post_form_1).await?; - let post_like_form_1 = PostLikeForm::new(post_1.id, sara.id, 1); + let post_like_form_1 = PostLikeForm::new(post_1.id, sara.id, true); let _post_like_1 = PostActions::like(pool, &post_like_form_1).await?; let post_form_2 = PostInsertForm::new("A test post radical".into(), sara.id, community.id); @@ -148,7 +148,7 @@ mod tests { CommentInsertForm::new(sara.id, post_1.id, "A test comment tubular".into()); let comment_1 = Comment::create(pool, &comment_form_1, None).await?; - let comment_like_form_1 = CommentLikeForm::new(sara.id, comment_1.id, 1); + let comment_like_form_1 = CommentLikeForm::new(sara.id, comment_1.id, true); let _comment_like_1 = CommentActions::like(pool, &comment_like_form_1).await?; let comment_form_2 = @@ -160,8 +160,8 @@ mod tests { PostView::read(pool, post_1.id, Some(&sara_local_user), instance.id, false).await?; assert_eq!(1, post_view_1.post.score); assert_eq!( - Some(1), - post_view_1.post_actions.and_then(|pa| pa.like_score) + Some(true), + post_view_1.post_actions.and_then(|pa| pa.vote_is_upvote) ); // Read saras comment to make sure it has a like @@ -169,8 +169,10 @@ mod tests { CommentView::read(pool, comment_1.id, Some(&sara_local_user), instance.id).await?; assert_eq!(1, comment_view_1.post.score); assert_eq!( - Some(1), - comment_view_1.comment_actions.and_then(|ca| ca.like_score) + Some(true), + comment_view_1 + .comment_actions + .and_then(|ca| ca.vote_is_upvote) ); // Remove the user data @@ -232,7 +234,10 @@ mod tests { let post_view_1 = PostView::read(pool, post_1.id, Some(&sara_local_user), instance.id, false).await?; assert_eq!(0, post_view_1.post.score); - assert_eq!(None, post_view_1.post_actions.and_then(|pa| pa.like_score)); + assert_eq!( + None, + post_view_1.post_actions.and_then(|pa| pa.vote_is_upvote) + ); // comment let comment_view_1 = @@ -240,7 +245,9 @@ mod tests { assert_eq!(0, comment_view_1.post.score); assert_eq!( None, - comment_view_1.comment_actions.and_then(|ca| ca.like_score) + comment_view_1 + .comment_actions + .and_then(|ca| ca.vote_is_upvote) ); // Now restore the content, and make sure it got appended diff --git a/crates/api/api_crud/src/comment/create.rs b/crates/api/api_crud/src/comment/create.rs index b1008973a..de62962c5 100644 --- a/crates/api/api_crud/src/comment/create.rs +++ b/crates/api/api_crud/src/comment/create.rs @@ -125,7 +125,7 @@ pub async fn create_comment( .send(&context); // You like your own comment by default - let like_form = CommentLikeForm::new(local_user_view.person.id, inserted_comment.id, 1); + let like_form = CommentLikeForm::new(local_user_view.person.id, inserted_comment.id, true); CommentActions::like(&mut context.pool(), &like_form).await?; diff --git a/crates/api/api_crud/src/post/create.rs b/crates/api/api_crud/src/post/create.rs index 833408ec0..f3daab171 100644 --- a/crates/api/api_crud/src/post/create.rs +++ b/crates/api/api_crud/src/post/create.rs @@ -161,7 +161,7 @@ pub async fn create_post( // They like their own post by default let person_id = local_user_view.person.id; let post_id = inserted_post.id; - let like_form = PostLikeForm::new(post_id, person_id, 1); + let like_form = PostLikeForm::new(post_id, person_id, true); PostActions::like(&mut context.pool(), &like_form).await?; diff --git a/crates/api/api_utils/src/send_activity.rs b/crates/api/api_utils/src/send_activity.rs index db525f28d..41788e502 100644 --- a/crates/api/api_utils/src/send_activity.rs +++ b/crates/api/api_utils/src/send_activity.rs @@ -62,8 +62,8 @@ pub enum SendActivityData { object_id: DbUrl, actor: Person, community: Community, - previous_score: Option, - new_score: i16, + previous_is_upvote: Option, + new_is_upvote: Option, }, FollowCommunity(Community, Person, bool), FollowMultiCommunity(MultiCommunity, Person, bool), diff --git a/crates/api/api_utils/src/utils.rs b/crates/api/api_utils/src/utils.rs index ee7745734..7235d08b7 100644 --- a/crates/api/api_utils/src/utils.rs +++ b/crates/api/api_utils/src/utils.rs @@ -311,7 +311,7 @@ pub fn check_comment_deleted_or_removed(comment: &Comment) -> LemmyResult<()> { } pub async fn check_local_vote_mode( - score: i16, + is_upvote: Option, post_or_comment_id: PostOrCommentId, local_site: &LocalSite, person_id: PersonId, @@ -322,8 +322,8 @@ pub async fn check_local_vote_mode( PostOrCommentId::Comment(_) => (local_site.comment_downvotes, local_site.comment_upvotes), }; - let downvote_fail = score == -1 && downvote_setting == FederationMode::Disable; - let upvote_fail = score == 1 && upvote_setting == FederationMode::Disable; + let downvote_fail = is_upvote == Some(false) && downvote_setting == FederationMode::Disable; + let upvote_fail = is_upvote == Some(true) && upvote_setting == FederationMode::Disable; // Undo previous vote for item if new vote fails if downvote_fail || upvote_fail { diff --git a/crates/apub/activities/src/create_or_update/comment.rs b/crates/apub/activities/src/create_or_update/comment.rs index f57e47131..c5f5d404d 100644 --- a/crates/apub/activities/src/create_or_update/comment.rs +++ b/crates/apub/activities/src/create_or_update/comment.rs @@ -144,7 +144,7 @@ impl Activity for CreateOrUpdateNote { let comment = ApubComment::from_json(self.object, context).await?; // author likes their own comment by default - let like_form = CommentLikeForm::new(comment.creator_id, comment.id, 1); + let like_form = CommentLikeForm::new(comment.creator_id, comment.id, true); CommentActions::like(&mut context.pool(), &like_form).await?; // Calculate initial hot_rank diff --git a/crates/apub/activities/src/create_or_update/post.rs b/crates/apub/activities/src/create_or_update/post.rs index 0a49ec06c..8023007da 100644 --- a/crates/apub/activities/src/create_or_update/post.rs +++ b/crates/apub/activities/src/create_or_update/post.rs @@ -144,7 +144,7 @@ impl Activity for CreateOrUpdatePage { let post = ApubPost::from_json(self.object, context).await?; // author likes their own post by default - let like_form = PostLikeForm::new(post.id, post.creator_id, 1); + let like_form = PostLikeForm::new(post.id, post.creator_id, true); PostActions::like(&mut context.pool(), &like_form).await?; // Calculate initial hot_rank for post diff --git a/crates/apub/activities/src/lib.rs b/crates/apub/activities/src/lib.rs index 3573b2f75..35b688649 100644 --- a/crates/apub/activities/src/lib.rs +++ b/crates/apub/activities/src/lib.rs @@ -253,15 +253,15 @@ pub async fn match_outgoing_activities( object_id, actor, community, - previous_score, - new_score, + previous_is_upvote, + new_is_upvote, } => { send_like_activity( object_id, actor, community, - previous_score, - new_score, + previous_is_upvote, + new_is_upvote, context, ) .await diff --git a/crates/apub/activities/src/protocol/voting/vote.rs b/crates/apub/activities/src/protocol/voting/vote.rs index 995e538b2..d51c97a24 100644 --- a/crates/apub/activities/src/protocol/voting/vote.rs +++ b/crates/apub/activities/src/protocol/voting/vote.rs @@ -5,7 +5,7 @@ use lemmy_apub_objects::{ objects::{community::ApubCommunity, person::ApubPerson, PostOrComment}, utils::protocol::InCommunity, }; -use lemmy_utils::error::{LemmyError, LemmyResult, UntranslatedError}; +use lemmy_utils::error::LemmyResult; use serde::{Deserialize, Serialize}; use strum::Display; use url::Url; @@ -26,24 +26,19 @@ pub enum VoteType { Dislike, } -impl TryFrom for VoteType { - type Error = LemmyError; - - fn try_from(value: i16) -> Result { - match value { - 1 => Ok(VoteType::Like), - -1 => Ok(VoteType::Dislike), - _ => Err(UntranslatedError::InvalidVoteValue.into()), +impl From for VoteType { + fn from(value: bool) -> Self { + if value { + VoteType::Like + } else { + VoteType::Dislike } } } -impl From<&VoteType> for i16 { - fn from(value: &VoteType) -> i16 { - match value { - VoteType::Like => 1, - VoteType::Dislike => -1, - } +impl From<&VoteType> for bool { + fn from(value: &VoteType) -> Self { + value == &VoteType::Like } } diff --git a/crates/apub/activities/src/voting/mod.rs b/crates/apub/activities/src/voting/mod.rs index 71ea88f40..938200ef2 100644 --- a/crates/apub/activities/src/voting/mod.rs +++ b/crates/apub/activities/src/voting/mod.rs @@ -38,8 +38,8 @@ pub(crate) async fn send_like_activity( object_id: DbUrl, actor: Person, community: Community, - previous_score: Option, - new_score: i16, + previous_is_upvote: Option, + new_is_upvote: Option, context: Data, ) -> LemmyResult<()> { let object_id: ObjectId = object_id.into(); @@ -47,13 +47,13 @@ pub(crate) async fn send_like_activity( let community: ApubCommunity = community.into(); let empty = ActivitySendTargets::empty(); - // score of 1 means upvote, -1 downvote, 0 undo a previous vote - if new_score != 0 { - let vote = Vote::new(object_id, &actor, new_score.try_into()?, &context)?; + if let Some(s) = new_is_upvote { + let vote = Vote::new(object_id, &actor, s.into(), &context)?; let activity = AnnouncableActivities::Vote(vote); send_activity_in_community(activity, &actor, &community, empty, false, &context).await } else { - let previous_vote_type = if previous_score.unwrap_or_default() > 0 { + // undo a previous vote + let previous_vote_type = if previous_is_upvote == Some(true) { VoteType::Like } else { VoteType::Dislike diff --git a/crates/db_schema/src/impls/comment.rs b/crates/db_schema/src/impls/comment.rs index 874876519..4c1f3c089 100644 --- a/crates/db_schema/src/impls/comment.rs +++ b/crates/db_schema/src/impls/comment.rs @@ -13,7 +13,6 @@ use crate::{ utils::{ functions::{coalesce, hot_rank}, get_conn, - validate_like, DbPool, DELETED_REPLACEMENT_TEXT, }, @@ -32,7 +31,7 @@ use diesel_ltree::{dsl::LtreeExtensions, Ltree}; use diesel_uplete::{uplete, UpleteCount}; use lemmy_db_schema_file::schema::{comment, comment_actions, community, post}; use lemmy_utils::{ - error::{LemmyErrorExt, LemmyErrorExt2, LemmyErrorType, LemmyResult}, + error::{LemmyErrorExt, LemmyErrorType, LemmyResult}, settings::structs::Settings, }; use url::Url; @@ -316,8 +315,6 @@ impl Likeable for CommentActions { async fn like(pool: &mut DbPool<'_>, form: &Self::Form) -> LemmyResult { let conn = &mut get_conn(pool).await?; - validate_like(form.like_score).with_lemmy_type(LemmyErrorType::CouldntCreate)?; - insert_into(comment_actions::table) .values(form) .on_conflict((comment_actions::comment_id, comment_actions::person_id)) @@ -335,8 +332,8 @@ impl Likeable for CommentActions { ) -> LemmyResult { let conn = &mut get_conn(pool).await?; uplete(comment_actions::table.find((person_id, comment_id))) - .set_null(comment_actions::like_score) - .set_null(comment_actions::liked_at) + .set_null(comment_actions::vote_is_upvote) + .set_null(comment_actions::voted_at) .get_result(conn) .await .with_lemmy_type(LemmyErrorType::CouldntCreate) @@ -349,8 +346,8 @@ impl Likeable for CommentActions { let conn = &mut get_conn(pool).await?; uplete(comment_actions::table.filter(comment_actions::person_id.eq(creator_id))) - .set_null(comment_actions::like_score) - .set_null(comment_actions::liked_at) + .set_null(comment_actions::vote_is_upvote) + .set_null(comment_actions::voted_at) .get_result(conn) .await .with_lemmy_type(LemmyErrorType::CouldntUpdate) @@ -367,8 +364,8 @@ impl Likeable for CommentActions { let conn = &mut get_conn(pool).await?; uplete(comment_actions::table.filter(comment_actions::comment_id.eq_any(comment_ids.clone()))) - .set_null(comment_actions::like_score) - .set_null(comment_actions::liked_at) + .set_null(comment_actions::vote_is_upvote) + .set_null(comment_actions::voted_at) .get_result(conn) .await .with_lemmy_type(LemmyErrorType::CouldntUpdate) @@ -509,10 +506,10 @@ mod tests { Comment::create(pool, &child_comment_form, Some(&inserted_comment.path)).await?; // Comment Like - let comment_like_form = CommentLikeForm::new(inserted_person.id, inserted_comment.id, 1); + let comment_like_form = CommentLikeForm::new(inserted_person.id, inserted_comment.id, true); let inserted_comment_like = CommentActions::like(pool, &comment_like_form).await?; - assert_eq!(Some(1), inserted_comment_like.like_score); + assert_eq!(Some(true), inserted_comment_like.vote_is_upvote); // Comment Saved let comment_saved_form = CommentSavedForm::new(inserted_person.id, inserted_comment.id); @@ -596,7 +593,7 @@ mod tests { let _inserted_child_comment = Comment::create(pool, &child_comment_form, Some(&inserted_comment.path)).await?; - let comment_like = CommentLikeForm::new(inserted_person.id, inserted_comment.id, 1); + let comment_like = CommentLikeForm::new(inserted_person.id, inserted_comment.id, true); CommentActions::like(pool, &comment_like).await?; @@ -607,7 +604,8 @@ mod tests { assert_eq!(0, comment_aggs_before_delete.downvotes); // Add a post dislike from the other person - let comment_dislike = CommentLikeForm::new(another_inserted_person.id, inserted_comment.id, -1); + let comment_dislike = + CommentLikeForm::new(another_inserted_person.id, inserted_comment.id, false); CommentActions::like(pool, &comment_dislike).await?; diff --git a/crates/db_schema/src/impls/person.rs b/crates/db_schema/src/impls/person.rs index 80ea92026..3741f050e 100644 --- a/crates/db_schema/src/impls/person.rs +++ b/crates/db_schema/src/impls/person.rs @@ -365,15 +365,11 @@ impl PersonActions { pool: &mut DbPool<'_>, person_id: PersonId, target_id: PersonId, - like_score: i16, + vote_is_upvote: bool, ) -> LemmyResult { let conn = &mut get_conn(pool).await?; - let (upvotes_inc, downvotes_inc) = match like_score { - 1 => (1, 0), - -1 => (0, 1), - _ => return Err(LemmyErrorType::NotFound.into()), - }; + let (upvotes_inc, downvotes_inc) = if vote_is_upvote { (1, 0) } else { (0, 1) }; let voted_at = Utc::now(); @@ -400,20 +396,16 @@ impl PersonActions { .with_lemmy_type(LemmyErrorType::NotFound) } - /// Removes a person like. A previous_score of zero throws an error. + /// Removes a person like. pub async fn remove_like( pool: &mut DbPool<'_>, person_id: PersonId, target_id: PersonId, - previous_score: i16, + previous_is_upvote: bool, ) -> LemmyResult { let conn = &mut get_conn(pool).await?; - let (upvotes_inc, downvotes_inc) = match previous_score { - 1 => (-1, 0), - -1 => (0, -1), - _ => return Err(LemmyErrorType::NotFound.into()), - }; + let (upvotes_inc, downvotes_inc) = if previous_is_upvote { (-1, 0) } else { (0, -1) }; let voted_at = Utc::now(); insert_into(person_actions::table) @@ -575,7 +567,7 @@ mod tests { ); let inserted_post = Post::create(pool, &new_post).await?; - let post_like = PostLikeForm::new(inserted_post.id, inserted_person.id, 1); + let post_like = PostLikeForm::new(inserted_post.id, inserted_person.id, true); let _inserted_post_like = PostActions::like(pool, &post_like).await?; let comment_form = CommentInsertForm::new( @@ -585,7 +577,7 @@ mod tests { ); let inserted_comment = Comment::create(pool, &comment_form, None).await?; - let mut comment_like = CommentLikeForm::new(inserted_person.id, inserted_comment.id, 1); + let mut comment_like = CommentLikeForm::new(inserted_person.id, inserted_comment.id, true); let _inserted_comment_like = CommentActions::like(pool, &comment_like).await?; @@ -598,7 +590,7 @@ mod tests { Comment::create(pool, &child_comment_form, Some(&inserted_comment.path)).await?; let child_comment_like = - CommentLikeForm::new(another_inserted_person.id, inserted_child_comment.id, 1); + CommentLikeForm::new(another_inserted_person.id, inserted_child_comment.id, true); let _inserted_child_comment_like = CommentActions::like(pool, &child_comment_like).await?; diff --git a/crates/db_schema/src/impls/post.rs b/crates/db_schema/src/impls/post.rs index adedb546d..68a3bcda7 100644 --- a/crates/db_schema/src/impls/post.rs +++ b/crates/db_schema/src/impls/post.rs @@ -16,7 +16,6 @@ use crate::{ functions::{coalesce, hot_rank, scaled_rank}, get_conn, now, - validate_like, DbPool, DELETED_REPLACEMENT_TEXT, FETCH_LIMIT_MAX, @@ -43,7 +42,7 @@ use lemmy_db_schema_file::{ schema::{community, local_user, person, post, post_actions}, }; use lemmy_utils::{ - error::{LemmyErrorExt, LemmyErrorExt2, LemmyErrorType, LemmyResult}, + error::{LemmyErrorExt, LemmyErrorType, LemmyResult}, settings::structs::Settings, }; use url::Url; @@ -313,7 +312,10 @@ impl Post { diesel::update(post::table.find(post_id)) .set(( post::hot_rank.eq(hot_rank(post::score, post::published_at)), - post::hot_rank_active.eq(hot_rank(post::score, post::newest_comment_time_necro_at)), + post::hot_rank_active.eq(hot_rank( + post::score, + coalesce(post::newest_comment_time_necro_at, post::published_at), + )), post::scaled_rank.eq(scaled_rank( post::score, post::published_at, @@ -349,8 +351,6 @@ impl Likeable for PostActions { async fn like(pool: &mut DbPool<'_>, form: &Self::Form) -> LemmyResult { let conn = &mut get_conn(pool).await?; - validate_like(form.like_score).with_lemmy_type(LemmyErrorType::CouldntCreate)?; - insert_into(post_actions::table) .values(form) .on_conflict((post_actions::post_id, post_actions::person_id)) @@ -369,8 +369,8 @@ impl Likeable for PostActions { ) -> LemmyResult { let conn = &mut get_conn(pool).await?; uplete(post_actions::table.find((person_id, post_id))) - .set_null(post_actions::like_score) - .set_null(post_actions::liked_at) + .set_null(post_actions::vote_is_upvote) + .set_null(post_actions::voted_at) .get_result(conn) .await .with_lemmy_type(LemmyErrorType::CouldntUpdate) @@ -383,8 +383,8 @@ impl Likeable for PostActions { let conn = &mut get_conn(pool).await?; uplete(post_actions::table.filter(post_actions::person_id.eq(person_id))) - .set_null(post_actions::like_score) - .set_null(post_actions::liked_at) + .set_null(post_actions::vote_is_upvote) + .set_null(post_actions::voted_at) .get_result(conn) .await .with_lemmy_type(LemmyErrorType::CouldntUpdate) @@ -400,8 +400,8 @@ impl Likeable for PostActions { let conn = &mut get_conn(pool).await?; uplete(post_actions::table.filter(post_actions::post_id.eq_any(post_ids.clone()))) - .set_null(post_actions::like_score) - .set_null(post_actions::liked_at) + .set_null(post_actions::vote_is_upvote) + .set_null(post_actions::voted_at) .get_result(conn) .await .with_lemmy_type(LemmyErrorType::CouldntUpdate) @@ -533,7 +533,10 @@ impl PostActions { .with_lemmy_type(LemmyErrorType::NotFound) } - pub async fn from_cursor(cursor: &PaginationCursor, pool: &mut DbPool<'_>) -> LemmyResult { + pub async fn from_cursor( + cursor: &PaginationCursor, + pool: &mut DbPool<'_>, + ) -> LemmyResult { let [(_, person_id), (_, post_id)] = cursor.prefixes_and_ids()?; Self::read(pool, PostId(post_id), PersonId(person_id)).await } @@ -688,8 +691,8 @@ mod tests { score: 1, hot_rank: RANK_DEFAULT, hot_rank_active: RANK_DEFAULT, - newest_comment_time_at: inserted_post.published_at, - newest_comment_time_necro_at: inserted_post.published_at, + newest_comment_time_at: None, + newest_comment_time_necro_at: None, report_count: 0, scaled_rank: RANK_DEFAULT, unresolved_report_count: 0, @@ -697,10 +700,10 @@ mod tests { }; // Post Like - let post_like_form = PostLikeForm::new(inserted_post.id, inserted_person.id, 1); + let post_like_form = PostLikeForm::new(inserted_post.id, inserted_person.id, true); let inserted_post_like = PostActions::like(pool, &post_like_form).await?; - assert_eq!(Some(1), inserted_post_like.like_score); + assert_eq!(Some(true), inserted_post_like.vote_is_upvote); // Post Save let post_saved_form = PostSavedForm::new(inserted_post.id, inserted_person.id); @@ -800,7 +803,7 @@ mod tests { let inserted_child_comment = Comment::create(pool, &child_comment_form, Some(&inserted_comment.path)).await?; - let post_like = PostLikeForm::new(inserted_post.id, inserted_person.id, 1); + let post_like = PostLikeForm::new(inserted_post.id, inserted_person.id, true); PostActions::like(pool, &post_like).await?; @@ -812,7 +815,7 @@ mod tests { assert_eq!(0, post_aggs_before_delete.downvotes); // Add a post dislike from the other person - let post_dislike = PostLikeForm::new(inserted_post.id, another_inserted_person.id, -1); + let post_dislike = PostLikeForm::new(inserted_post.id, another_inserted_person.id, false); PostActions::like(pool, &post_dislike).await?; diff --git a/crates/db_schema/src/lib.rs b/crates/db_schema/src/lib.rs index ce81aaae5..eca656ae4 100644 --- a/crates/db_schema/src/lib.rs +++ b/crates/db_schema/src/lib.rs @@ -251,9 +251,9 @@ pub type Person2AliasAllColumnsTuple = ( #[cfg(feature = "full")] /// A helper tuple for more my instance persons actions pub type MyInstancePersonsActionsAllColumnsTuple = ( + AliasedField, AliasedField, AliasedField, - AliasedField, AliasedField, AliasedField, AliasedField, diff --git a/crates/db_schema/src/source/combined/person_liked.rs b/crates/db_schema/src/source/combined/person_liked.rs index 91b84b352..262369d14 100644 --- a/crates/db_schema/src/source/combined/person_liked.rs +++ b/crates/db_schema/src/source/combined/person_liked.rs @@ -18,10 +18,10 @@ use serde_with::skip_serializing_none; #[cfg_attr(feature = "full", cursor_keys_module(name = person_liked_combined_keys))] /// A combined person_liked table. pub struct PersonLikedCombined { - pub liked_at: DateTime, - pub like_score: i16, + pub voted_at: DateTime, + pub id: PersonLikedCombinedId, pub person_id: PersonId, pub post_id: Option, pub comment_id: Option, - pub id: PersonLikedCombinedId, + pub vote_is_upvote: bool, } diff --git a/crates/db_schema/src/source/combined/search.rs b/crates/db_schema/src/source/combined/search.rs index e523ecc72..bc2267d22 100644 --- a/crates/db_schema/src/source/combined/search.rs +++ b/crates/db_schema/src/source/combined/search.rs @@ -34,35 +34,3 @@ pub struct SearchCombined { pub id: SearchCombinedId, pub multi_community_id: Option, } - -#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))] -#[cfg_attr(feature = "full", diesel(table_name = search_combined))] -pub struct SearchCombinedPostInsertForm { - pub published_at: DateTime, - pub score: i32, - pub post_id: PostId, -} - -#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))] -#[cfg_attr(feature = "full", diesel(table_name = search_combined))] -pub struct SearchCombinedCommentInsertForm { - pub published_at: DateTime, - pub score: i32, - pub comment_id: CommentId, -} - -#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))] -#[cfg_attr(feature = "full", diesel(table_name = search_combined))] -pub struct SearchCombinedCommunityInsertForm { - pub published_at: DateTime, - pub score: i32, - pub community_id: CommunityId, -} - -#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))] -#[cfg_attr(feature = "full", diesel(table_name = search_combined))] -pub struct SearchCombinedPersonInsertForm { - pub published_at: DateTime, - pub score: i32, - pub person_id: PersonId, -} diff --git a/crates/db_schema/src/source/comment.rs b/crates/db_schema/src/source/comment.rs index 886d5b143..b87021008 100644 --- a/crates/db_schema/src/source/comment.rs +++ b/crates/db_schema/src/source/comment.rs @@ -55,9 +55,9 @@ pub struct Comment { /// The total number of children in this comment branch. pub child_count: i32, #[serde(skip)] - pub hot_rank: f64, + pub hot_rank: f32, #[serde(skip)] - pub controversy_rank: f64, + pub controversy_rank: f32, pub report_count: i16, pub unresolved_report_count: i16, /// If a local user comments in a remote community, the comment is hidden until it is confirmed @@ -130,16 +130,16 @@ pub struct CommentUpdateForm { #[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))] #[cfg_attr(feature = "ts-rs", ts(optional_fields, export))] pub struct CommentActions { + /// When the comment was upvoted or downvoted. + pub voted_at: Option>, + /// When the comment was saved. + pub saved_at: Option>, #[serde(skip)] pub person_id: PersonId, #[serde(skip)] pub comment_id: CommentId, - /// The like / score for the comment. - pub like_score: Option, - /// When the comment was liked. - pub liked_at: Option>, - /// When the comment was saved. - pub saved_at: Option>, + /// True if upvoted, false if downvoted. Upvote is greater than downvote. + pub vote_is_upvote: Option, } #[derive(Clone, derive_new::new)] @@ -151,9 +151,9 @@ pub struct CommentActions { pub struct CommentLikeForm { pub person_id: PersonId, pub comment_id: CommentId, - pub like_score: i16, + pub vote_is_upvote: bool, #[new(value = "Utc::now()")] - pub liked_at: DateTime, + pub voted_at: DateTime, } #[derive(derive_new::new)] diff --git a/crates/db_schema/src/source/community.rs b/crates/db_schema/src/source/community.rs index dc7b7bd94..9b2a75e63 100644 --- a/crates/db_schema/src/source/community.rs +++ b/crates/db_schema/src/source/community.rs @@ -90,13 +90,13 @@ pub struct Community { /// The number of users with any activity in the last year. pub users_active_half_year: i32, #[serde(skip)] - pub hot_rank: f64, + pub hot_rank: f32, pub subscribers_local: i32, - pub report_count: i16, - pub unresolved_report_count: i16, /// Number of any interactions over the last month. #[serde(skip)] pub interactions_month: i32, + pub report_count: i16, + pub unresolved_report_count: i16, pub local_removed: bool, } @@ -195,17 +195,8 @@ pub struct CommunityUpdateForm { #[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))] #[cfg_attr(feature = "ts-rs", ts(optional_fields, export))] pub struct CommunityActions { - #[serde(skip)] - pub person_id: PersonId, - #[serde(skip)] - pub community_id: CommunityId, /// When the community was followed. pub followed_at: Option>, - /// The state of the community follow. - pub follow_state: Option, - /// The approver of the community follow. - #[serde(skip)] - pub follow_approver_id: Option, /// When the community was blocked. pub blocked_at: Option>, /// When this user became a moderator. @@ -214,6 +205,15 @@ pub struct CommunityActions { pub received_ban_at: Option>, /// When their ban expires. pub ban_expires_at: Option>, + #[serde(skip)] + pub person_id: PersonId, + #[serde(skip)] + pub community_id: CommunityId, + /// The state of the community follow. + pub follow_state: Option, + /// The approver of the community follow. + #[serde(skip)] + pub follow_approver_id: Option, pub notifications: Option, } diff --git a/crates/db_schema/src/source/instance.rs b/crates/db_schema/src/source/instance.rs index 6374007ea..24a899faa 100644 --- a/crates/db_schema/src/source/instance.rs +++ b/crates/db_schema/src/source/instance.rs @@ -55,12 +55,12 @@ pub struct InstanceForm { #[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))] #[cfg_attr(feature = "ts-rs", ts(optional_fields, export))] pub struct InstanceActions { + /// When the instance's communities were blocked. + pub blocked_communities_at: Option>, #[serde(skip)] pub person_id: PersonId, #[serde(skip)] pub instance_id: InstanceId, - /// When the instance's communities were blocked. - pub blocked_communities_at: Option>, /// When this user received a site ban. pub received_ban_at: Option>, /// When their ban expires. diff --git a/crates/db_schema/src/source/person.rs b/crates/db_schema/src/source/person.rs index ca7189116..feab661ce 100644 --- a/crates/db_schema/src/source/person.rs +++ b/crates/db_schema/src/source/person.rs @@ -134,16 +134,16 @@ pub struct PersonUpdateForm { #[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))] #[cfg_attr(feature = "ts-rs", ts(optional_fields, export))] pub struct PersonActions { + #[serde(skip)] + pub followed_at: Option>, + /// When the person was blocked. + pub blocked_at: Option>, #[serde(skip)] pub person_id: PersonId, #[serde(skip)] pub target_id: PersonId, #[serde(skip)] - pub followed_at: Option>, - #[serde(skip)] pub follow_pending: Option, - /// When the person was blocked. - pub blocked_at: Option>, /// When the person was noted. pub noted_at: Option>, /// A note about the person. diff --git a/crates/db_schema/src/source/post.rs b/crates/db_schema/src/source/post.rs index 37701caa4..9df53e437 100644 --- a/crates/db_schema/src/source/post.rs +++ b/crates/db_schema/src/source/post.rs @@ -62,24 +62,24 @@ pub struct Post { pub alt_text: Option, /// Time at which the post will be published. None means publish immediately. pub scheduled_publish_time_at: Option>, + #[serde(skip)] + /// A newest comment time, limited to 2 days, to prevent necrobumping + pub newest_comment_time_necro_at: Option>, + /// The time of the newest comment in the post, if the post has any comments. + pub newest_comment_time_at: Option>, pub comments: i32, pub score: i32, pub upvotes: i32, pub downvotes: i32, #[serde(skip)] - /// A newest comment time, limited to 2 days, to prevent necrobumping - pub newest_comment_time_necro_at: DateTime, - /// The time of the newest comment in the post. - pub newest_comment_time_at: DateTime, + pub hot_rank: f32, #[serde(skip)] - pub hot_rank: f64, + pub hot_rank_active: f32, #[serde(skip)] - pub hot_rank_active: f64, - #[serde(skip)] - pub controversy_rank: f64, + pub controversy_rank: f32, /// A rank that amplifies smaller communities #[serde(skip)] - pub scaled_rank: f64, + pub scaled_rank: f32, pub report_count: i16, pub unresolved_report_count: i16, /// If a local user posts in a remote community, the comment is hidden until it is confirmed @@ -191,25 +191,25 @@ pub struct PostUpdateForm { #[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))] #[cfg_attr(feature = "ts-rs", ts(optional_fields, export))] pub struct PostActions { - #[serde(skip)] - pub person_id: PersonId, - #[serde(skip)] - pub post_id: PostId, /// When the post was read. pub read_at: Option>, /// When was the last time you read the comments. pub read_comments_at: Option>, + /// When the post was saved. + pub saved_at: Option>, + /// When the post was upvoted or downvoted. + pub voted_at: Option>, + /// When the post was hidden. + pub hidden_at: Option>, + #[serde(skip)] + pub person_id: PersonId, + #[serde(skip)] + pub post_id: PostId, /// The number of comments you read last. Subtract this from total comments to get an unread /// count. pub read_comments_amount: Option, - /// When the post was saved. - pub saved_at: Option>, - /// When the post was liked. - pub liked_at: Option>, - /// The like / score of the post. - pub like_score: Option, - /// When the post was hidden. - pub hidden_at: Option>, + /// True if upvoted, false if downvoted. Upvote is greater than downvote. + pub vote_is_upvote: Option, pub notifications: Option, } @@ -222,9 +222,9 @@ pub struct PostActions { pub struct PostLikeForm { pub post_id: PostId, pub person_id: PersonId, - pub like_score: i16, + pub vote_is_upvote: bool, #[new(value = "Utc::now()")] - pub liked_at: DateTime, + pub voted_at: DateTime, } #[derive(derive_new::new)] diff --git a/crates/db_schema/src/traits.rs b/crates/db_schema/src/traits.rs index 8dfe3df0e..261019bae 100644 --- a/crates/db_schema/src/traits.rs +++ b/crates/db_schema/src/traits.rs @@ -7,8 +7,11 @@ use crate::{ use diesel::{ associations::HasTable, dsl, + expression::{Expression, Selectable}, + pg::Pg, query_builder::{DeleteStatement, IntoUpdateTarget}, - query_dsl::methods::{FindDsl, LimitDsl}, + query_dsl::methods::{FindDsl, LimitDsl, SelectDsl}, + SelectableHelper, Table, }; use diesel_async::{ @@ -34,14 +37,17 @@ pub type PrimaryKey = <::Table as Table>::PrimaryKey; // Trying to create default implementations for `create` and `update` results in a lifetime mess and // weird compile errors. https://github.com/rust-lang/rust/issues/102211 -pub trait Crud: HasTable + Sized +pub trait Crud: HasTable + Sized + Selectable where Self::Table: FindDsl, - Find: LimitDsl + IntoUpdateTarget + Send, + Find: LimitDsl + IntoUpdateTarget + SelectDsl> + Send, Delete>: ExecuteDsl + Send + 'static, + dsl::Select, dsl::AsSelect>: LimitDsl + Send, + dsl::AsSelect: Expression, // Used by `RunQueryDsl::first` - dsl::Limit>: LoadQuery<'static, AsyncPgConnection, Self> + Send + 'static, + dsl::Limit, dsl::AsSelect>>: + LoadQuery<'static, AsyncPgConnection, Self> + Send + 'static, { type InsertForm; type UpdateForm; @@ -57,7 +63,8 @@ where Self: Send, { async { - let query: Find = Self::table().find(id); + let query: dsl::Select, dsl::AsSelect> = + Self::table().find(id).select(Self::as_select()); let conn = &mut *get_conn(pool).await?; query .first(conn) diff --git a/crates/db_schema/src/utils.rs b/crates/db_schema/src/utils.rs index 751671388..956133377 100644 --- a/crates/db_schema/src/utils.rs +++ b/crates/db_schema/src/utils.rs @@ -60,7 +60,7 @@ const FETCH_LIMIT_DEFAULT: i64 = 20; pub const FETCH_LIMIT_MAX: usize = 50; pub const SITEMAP_LIMIT: i64 = 50000; pub const SITEMAP_DAYS: TimeDelta = TimeDelta::days(31); -pub const RANK_DEFAULT: f64 = 0.0001; +pub const RANK_DEFAULT: f32 = 0.0001; pub type ActualDbPool = Pool; @@ -218,6 +218,28 @@ where } } +pub struct CoalesceKey(pub A, pub B); + +impl CursorKey for CoalesceKey +where + A: CursorKey>, + B: CursorKey, +{ + type SqlType = B::SqlType; + type CursorValue = functions::coalesce; + type SqlValue = functions::coalesce; + + fn get_cursor_value(cursor: &C) -> Self::CursorValue { + // TODO: for slight optimization, use unwrap_or_else here (this requires the CursorKey trait to + // be changed to allow non-binded CursorValue) + functions::coalesce(A::get_cursor_value(cursor), B::get_cursor_value(cursor)) + } + + fn get_sql_value() -> Self::SqlValue { + functions::coalesce(A::get_sql_value(), B::get_sql_value()) + } +} + /// Includes an SQL comment before `T`, which can be used to label auto_explain output #[derive(QueryId)] pub struct Commented { @@ -490,12 +512,12 @@ pub mod functions { define_sql_function! { #[sql_name = "r.hot_rank"] - fn hot_rank(score: Int4, time: Timestamptz) -> Double; + fn hot_rank(score: Int4, time: Timestamptz) -> Float; } define_sql_function! { #[sql_name = "r.scaled_rank"] - fn scaled_rank(score: Int4, time: Timestamptz, interactions_month: Int4) -> Double; + fn scaled_rank(score: Int4, time: Timestamptz, interactions_month: Int4) -> Float; } define_sql_function!(fn lower(x: Text) -> Text); @@ -572,18 +594,6 @@ pub(crate) fn format_actor_url( Ok(Url::parse(&url)?) } -/// Make sure the like score is 1, or -1 -/// -/// Uses a default NotFound error, that you should map to -/// CouldntLikeComment/CouldntLikePost. -pub(crate) fn validate_like(like_score: i16) -> LemmyResult<()> { - if [-1, 1].contains(&like_score) { - Ok(()) - } else { - Err(LemmyErrorType::NotFound.into()) - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/crates/db_schema/src/utils/queries/selects.rs b/crates/db_schema/src/utils/queries/selects.rs index 80881e552..1d338a2d9 100644 --- a/crates/db_schema/src/utils/queries/selects.rs +++ b/crates/db_schema/src/utils/queries/selects.rs @@ -315,12 +315,12 @@ pub fn post_select_remove_deletes() -> _ { post::url_content_type, post::alt_text, post::scheduled_publish_time_at, + post::newest_comment_time_necro_at, + post::newest_comment_time_at, post::comments, post::score, post::upvotes, post::downvotes, - post::newest_comment_time_necro_at, - post::newest_comment_time_at, post::hot_rank, post::hot_rank_active, post::controversy_rank, diff --git a/crates/db_schema_file/src/schema.rs b/crates/db_schema_file/src/schema.rs index a2257182f..7ee5c79e3 100644 --- a/crates/db_schema_file/src/schema.rs +++ b/crates/db_schema_file/src/schema.rs @@ -184,8 +184,8 @@ diesel::table! { upvotes -> Int4, downvotes -> Int4, child_count -> Int4, - hot_rank -> Float8, - controversy_rank -> Float8, + hot_rank -> Float4, + controversy_rank -> Float4, report_count -> Int2, unresolved_report_count -> Int2, federation_pending -> Bool, @@ -195,11 +195,11 @@ diesel::table! { diesel::table! { comment_actions (person_id, comment_id) { + voted_at -> Nullable, + saved_at -> Nullable, person_id -> Int4, comment_id -> Int4, - like_score -> Nullable, - liked_at -> Nullable, - saved_at -> Nullable, + vote_is_upvote -> Nullable, } } @@ -263,11 +263,11 @@ diesel::table! { users_active_week -> Int4, users_active_month -> Int4, users_active_half_year -> Int4, - hot_rank -> Float8, + hot_rank -> Float4, subscribers_local -> Int4, + interactions_month -> Int4, report_count -> Int2, unresolved_report_count -> Int2, - interactions_month -> Int4, local_removed -> Bool, } } @@ -278,15 +278,15 @@ diesel::table! { use super::sql_types::CommunityNotificationsModeEnum; community_actions (person_id, community_id) { - person_id -> Int4, - community_id -> Int4, followed_at -> Nullable, - follow_state -> Nullable, - follow_approver_id -> Nullable, blocked_at -> Nullable, became_moderator_at -> Nullable, received_ban_at -> Nullable, ban_expires_at -> Nullable, + person_id -> Int4, + community_id -> Int4, + follow_state -> Nullable, + follow_approver_id -> Nullable, notifications -> Nullable, } } @@ -410,9 +410,9 @@ diesel::table! { diesel::table! { instance_actions (person_id, instance_id) { + blocked_communities_at -> Nullable, person_id -> Int4, instance_id -> Int4, - blocked_communities_at -> Nullable, received_ban_at -> Nullable, ban_expires_at -> Nullable, blocked_persons_at -> Nullable, @@ -866,11 +866,11 @@ diesel::table! { diesel::table! { person_actions (person_id, target_id) { + followed_at -> Nullable, + blocked_at -> Nullable, person_id -> Int4, target_id -> Int4, - followed_at -> Nullable, follow_pending -> Nullable, - blocked_at -> Nullable, noted_at -> Nullable, note -> Nullable, voted_at -> Nullable, @@ -890,12 +890,12 @@ diesel::table! { diesel::table! { person_liked_combined (id) { - liked_at -> Timestamptz, - like_score -> Int2, + voted_at -> Timestamptz, + id -> Int4, person_id -> Int4, post_id -> Nullable, comment_id -> Nullable, - id -> Int4, + vote_is_upvote -> Bool, } } @@ -938,16 +938,16 @@ diesel::table! { url_content_type -> Nullable, alt_text -> Nullable, scheduled_publish_time_at -> Nullable, + newest_comment_time_necro_at -> Nullable, + newest_comment_time_at -> Nullable, comments -> Int4, score -> Int4, upvotes -> Int4, downvotes -> Int4, - newest_comment_time_necro_at -> Timestamptz, - newest_comment_time_at -> Timestamptz, - hot_rank -> Float8, - hot_rank_active -> Float8, - controversy_rank -> Float8, - scaled_rank -> Float8, + hot_rank -> Float4, + hot_rank_active -> Float4, + controversy_rank -> Float4, + scaled_rank -> Float4, report_count -> Int2, unresolved_report_count -> Int2, federation_pending -> Bool, @@ -961,15 +961,15 @@ diesel::table! { use super::sql_types::PostNotificationsModeEnum; post_actions (person_id, post_id) { - person_id -> Int4, - post_id -> Int4, read_at -> Nullable, read_comments_at -> Nullable, - read_comments_amount -> Nullable, saved_at -> Nullable, - liked_at -> Nullable, - like_score -> Nullable, + voted_at -> Nullable, hidden_at -> Nullable, + person_id -> Int4, + post_id -> Int4, + read_comments_amount -> Nullable, + vote_is_upvote -> Nullable, notifications -> Nullable, } } diff --git a/crates/db_schema_setup/replaceable_schema/triggers.sql b/crates/db_schema_setup/replaceable_schema/triggers.sql index aa1704918..8f8bfc48f 100644 --- a/crates/db_schema_setup/replaceable_schema/triggers.sql +++ b/crates/db_schema_setup/replaceable_schema/triggers.sql @@ -29,8 +29,8 @@ BEGIN score = a.score + diff.upvotes - diff.downvotes, upvotes = a.upvotes + diff.upvotes, downvotes = a.downvotes + diff.downvotes, controversy_rank = r.controversy_rank ((a.upvotes + diff.upvotes)::numeric, (a.downvotes + diff.downvotes)::numeric) FROM ( SELECT - (thing_actions).thing_id, coalesce(sum(count_diff) FILTER (WHERE (thing_actions).like_score = 1), 0) AS upvotes, coalesce(sum(count_diff) FILTER (WHERE (thing_actions).like_score != 1), 0) AS downvotes FROM select_old_and_new_rows AS old_and_new_rows - WHERE (thing_actions).like_score IS NOT NULL GROUP BY (thing_actions).thing_id) AS diff + (thing_actions).thing_id, coalesce(sum(count_diff) FILTER (WHERE (thing_actions).vote_is_upvote), 0) AS upvotes, coalesce(sum(count_diff) FILTER (WHERE NOT (thing_actions).vote_is_upvote), 0) AS downvotes FROM select_old_and_new_rows AS old_and_new_rows + WHERE (thing_actions).vote_is_upvote IS NOT NULL GROUP BY (thing_actions).thing_id) AS diff WHERE a.id = diff.thing_id AND (diff.upvotes, diff.downvotes) != (0, 0) @@ -370,9 +370,6 @@ BEGIN IF NEW.local THEN NEW.ap_id = coalesce(NEW.ap_id, r.local_url ('/post/' || NEW.id::text)); END IF; - -- Set aggregates - NEW.newest_comment_time_at = NEW.published_at; - NEW.newest_comment_time_necro_at = NEW.published_at; RETURN NEW; END $$; @@ -516,26 +513,26 @@ BEGIN WHERE p.person_id = OLD.person_id AND p.thing_id = OLD.thing_id; ELSIF (TG_OP = 'INSERT') THEN - IF NEW.liked_at IS NOT NULL AND ( + IF NEW.voted_at IS NOT NULL AND ( SELECT local FROM person WHERE id = NEW.person_id) = TRUE THEN - INSERT INTO person_liked_combined (liked_at, like_score, person_id, thing_id) - VALUES (NEW.liked_at, NEW.like_score, NEW.person_id, NEW.thing_id); + INSERT INTO person_liked_combined (voted_at, vote_is_upvote, person_id, thing_id) + VALUES (NEW.voted_at, NEW.vote_is_upvote, NEW.person_id, NEW.thing_id); END IF; ELSIF (TG_OP = 'UPDATE') THEN - IF NEW.liked_at IS NOT NULL AND ( + IF NEW.voted_at IS NOT NULL AND ( SELECT local FROM person WHERE id = NEW.person_id) = TRUE THEN - INSERT INTO person_liked_combined (liked_at, like_score, person_id, thing_id) - VALUES (NEW.liked_at, NEW.like_score, NEW.person_id, NEW.thing_id); + INSERT INTO person_liked_combined (voted_at, vote_is_upvote, person_id, thing_id) + VALUES (NEW.voted_at, NEW.vote_is_upvote, NEW.person_id, NEW.thing_id); -- If liked gets set as null, delete the row ELSE DELETE FROM person_liked_combined AS p @@ -546,7 +543,7 @@ BEGIN RETURN NULL; END $$; CREATE TRIGGER person_liked_combined - AFTER INSERT OR DELETE OR UPDATE OF liked_at ON thing_actions + AFTER INSERT OR DELETE OR UPDATE OF voted_at ON thing_actions FOR EACH ROW EXECUTE FUNCTION r.person_liked_combined_change_values_thing ( ); $b$, diff --git a/crates/db_schema_setup/replaceable_schema/utils.sql b/crates/db_schema_setup/replaceable_schema/utils.sql index cd37c9bbf..38aa240b9 100644 --- a/crates/db_schema_setup/replaceable_schema/utils.sql +++ b/crates/db_schema_setup/replaceable_schema/utils.sql @@ -1,7 +1,7 @@ -- Each calculation used in triggers should be a single SQL language -- expression so it can be inlined in migrations. CREATE FUNCTION r.controversy_rank (upvotes numeric, downvotes numeric) - RETURNS float + RETURNS real LANGUAGE sql IMMUTABLE PARALLEL SAFE RETURN CASE WHEN downvotes <= 0 OR upvotes <= 0 THEN @@ -16,7 +16,7 @@ CREATE FUNCTION r.controversy_rank (upvotes numeric, downvotes numeric) END; CREATE FUNCTION r.hot_rank (score numeric, published_at timestamp with time zone) - RETURNS double precision + RETURNS real LANGUAGE sql IMMUTABLE PARALLEL SAFE RETURN -- after a week, it will default to 0. @@ -34,7 +34,7 @@ now() - published_at) < '7 days' THEN END; CREATE FUNCTION r.scaled_rank (score numeric, published_at timestamp with time zone, interactions_month numeric) - RETURNS double precision + RETURNS real LANGUAGE sql IMMUTABLE PARALLEL SAFE -- Add 2 to avoid divide by zero errors @@ -154,14 +154,14 @@ $a$; -- Edit community aggregates to include voters as active users CREATE OR REPLACE FUNCTION r.community_aggregates_activity (i text) RETURNS TABLE ( - count_ bigint, + count_ integer, community_id_ integer) LANGUAGE plpgsql AS $$ BEGIN RETURN query SELECT - count(*), + count(*)::integer, community_id FROM ( SELECT @@ -193,7 +193,7 @@ BEGIN INNER JOIN post p ON pa.post_id = p.id INNER JOIN person pe ON pa.person_id = pe.id WHERE - pa.liked_at > ('now'::timestamp - i::interval) + pa.voted_at > ('now'::timestamp - i::interval) AND pe.bot_account = FALSE UNION SELECT @@ -205,7 +205,7 @@ BEGIN INNER JOIN post p ON c.post_id = p.id INNER JOIN person pe ON ca.person_id = pe.id WHERE - ca.liked_at > ('now'::timestamp - i::interval) + ca.voted_at > ('now'::timestamp - i::interval) AND pe.bot_account = FALSE) a GROUP BY community_id; @@ -215,14 +215,14 @@ $$; -- Community aggregate function for adding up total number of interactions CREATE OR REPLACE FUNCTION r.community_aggregates_interactions (i text) RETURNS TABLE ( - count_ bigint, + count_ integer, community_id_ integer) LANGUAGE plpgsql AS $$ BEGIN RETURN query SELECT - COALESCE(sum(comments + upvotes + downvotes)::bigint, 0) AS count_, + COALESCE(sum(comments + upvotes + downvotes)::integer, 0) AS count_, community_id AS community_id_ FROM post @@ -270,7 +270,7 @@ BEGIN post_actions pa INNER JOIN person pe ON pa.person_id = pe.id WHERE - pa.liked_at > ('now'::timestamp - i::interval) + pa.voted_at > ('now'::timestamp - i::interval) AND pe.local = TRUE AND pe.bot_account = FALSE UNION @@ -280,7 +280,7 @@ BEGIN comment_actions ca INNER JOIN person pe ON ca.person_id = pe.id WHERE - ca.liked_at > ('now'::timestamp - i::interval) + ca.voted_at > ('now'::timestamp - i::interval) AND pe.local = TRUE AND pe.bot_account = FALSE) a; RETURN count_; diff --git a/crates/db_views/comment/src/api.rs b/crates/db_views/comment/src/api.rs index 0f8d0be57..e41fa5f03 100644 --- a/crates/db_views/comment/src/api.rs +++ b/crates/db_views/comment/src/api.rs @@ -32,8 +32,7 @@ pub struct CreateComment { /// Like a comment. pub struct CreateCommentLike { pub comment_id: CommentId, - /// Must be -1, 0, or 1 . - pub score: i16, + pub is_upvote: Option, } #[skip_serializing_none] diff --git a/crates/db_views/comment/src/impls.rs b/crates/db_views/comment/src/impls.rs index 4ad85fb99..6e96c26d5 100644 --- a/crates/db_views/comment/src/impls.rs +++ b/crates/db_views/comment/src/impls.rs @@ -457,7 +457,7 @@ mod tests { ) ); - let comment_like_form = CommentLikeForm::new(inserted_timmy_person.id, comment_0.id, 1); + let comment_like_form = CommentLikeForm::new(inserted_timmy_person.id, comment_0.id, true); CommentActions::like(pool, &comment_like_form).await?; @@ -513,7 +513,7 @@ mod tests { assert!(read_comment_views_with_person[0] .comment_actions .as_ref() - .is_some_and(|x| x.like_score == Some(1))); + .is_some_and(|x| x.vote_is_upvote == Some(true))); assert!(read_comment_views_with_person[0].can_mod); // Make sure its 1, not showing the blocked comment diff --git a/crates/db_views/person_liked_combined/src/impls.rs b/crates/db_views/person_liked_combined/src/impls.rs index cbfcbfba4..91fb7dd7f 100644 --- a/crates/db_views/person_liked_combined/src/impls.rs +++ b/crates/db_views/person_liked_combined/src/impls.rs @@ -6,6 +6,7 @@ use crate::{ PostView, }; use diesel::{ + dsl::not, BoolExpressionMethods, ExpressionMethods, JoinOnDsl, @@ -179,8 +180,8 @@ impl PersonLikedCombinedQuery { if let Some(like_type) = self.like_type { query = match like_type { LikeType::All => query, - LikeType::LikedOnly => query.filter(person_liked_combined::like_score.eq(1)), - LikeType::DislikedOnly => query.filter(person_liked_combined::like_score.eq(-1)), + LikeType::LikedOnly => query.filter(person_liked_combined::vote_is_upvote), + LikeType::DislikedOnly => query.filter(not(person_liked_combined::vote_is_upvote)), } } @@ -192,7 +193,7 @@ impl PersonLikedCombinedQuery { None, self.page_back, ) - .then_order_by(key::liked_at) + .then_order_by(key::voted_at) // Tie breaker .then_order_by(key::id); @@ -366,13 +367,13 @@ mod tests { assert_eq!(0, timmy_liked.len()); // Like a few things - let like_sara_comment_2 = CommentLikeForm::new(data.timmy.id, data.sara_comment_2.id, 1); + let like_sara_comment_2 = CommentLikeForm::new(data.timmy.id, data.sara_comment_2.id, true); CommentActions::like(pool, &like_sara_comment_2).await?; - let dislike_sara_comment = CommentLikeForm::new(data.timmy.id, data.sara_comment.id, -1); + let dislike_sara_comment = CommentLikeForm::new(data.timmy.id, data.sara_comment.id, false); CommentActions::like(pool, &dislike_sara_comment).await?; - let post_like_form = PostLikeForm::new(data.timmy_post.id, data.timmy.id, 1); + let post_like_form = PostLikeForm::new(data.timmy_post.id, data.timmy.id, true); PostActions::like(pool, &post_like_form).await?; let timmy_liked_all = PersonLikedCombinedQuery::default() @@ -384,7 +385,10 @@ mod tests { if let PersonLikedCombinedView::Post(v) = &timmy_liked_all[0] { assert_eq!(data.timmy_post.id, v.post.id); assert_eq!(data.timmy.id, v.post.creator_id); - assert_eq!(Some(1), v.post_actions.as_ref().and_then(|l| l.like_score)); + assert_eq!( + Some(true), + v.post_actions.as_ref().and_then(|l| l.vote_is_upvote) + ); } else { panic!("wrong type"); } @@ -392,8 +396,8 @@ mod tests { assert_eq!(data.sara_comment.id, v.comment.id); assert_eq!(data.sara.id, v.comment.creator_id); assert_eq!( - Some(-1), - v.comment_actions.as_ref().and_then(|l| l.like_score) + Some(false), + v.comment_actions.as_ref().and_then(|l| l.vote_is_upvote) ); } else { panic!("wrong type"); @@ -402,8 +406,8 @@ mod tests { assert_eq!(data.sara_comment_2.id, v.comment.id); assert_eq!(data.sara.id, v.comment.creator_id); assert_eq!( - Some(1), - v.comment_actions.as_ref().and_then(|l| l.like_score) + Some(true), + v.comment_actions.as_ref().and_then(|l| l.vote_is_upvote) ); } else { panic!("wrong type"); @@ -421,8 +425,8 @@ mod tests { assert_eq!(data.sara_comment.id, v.comment.id); assert_eq!(data.sara.id, v.comment.creator_id); assert_eq!( - Some(-1), - v.comment_actions.as_ref().and_then(|l| l.like_score) + Some(false), + v.comment_actions.as_ref().and_then(|l| l.vote_is_upvote) ); } else { panic!("wrong type"); diff --git a/crates/db_views/post/src/api.rs b/crates/db_views/post/src/api.rs index 1e74204a7..dfc937846 100644 --- a/crates/db_views/post/src/api.rs +++ b/crates/db_views/post/src/api.rs @@ -38,8 +38,7 @@ pub struct CreatePost { /// Like a post. pub struct CreatePostLike { pub post_id: PostId, - /// Score must be -1, 0, or 1. - pub score: i16, + pub is_upvote: Option, } #[derive(Debug, Serialize, Deserialize, Clone, Copy, Default, PartialEq, Eq, Hash)] diff --git a/crates/db_views/post/src/impls.rs b/crates/db_views/post/src/impls.rs index 01ea4759d..3f0c2616c 100644 --- a/crates/db_views/post/src/impls.rs +++ b/crates/db_views/post/src/impls.rs @@ -53,6 +53,7 @@ use lemmy_db_schema::{ }, }, seconds_to_pg_interval, + CoalesceKey, Commented, DbPool, }, @@ -529,7 +530,7 @@ impl PostQuery<'_> { Scaled => pq.then_order_by(key::scaled_rank), Controversial => pq.then_order_by(key::controversy_rank), New | Old => pq.then_order_by(key::published_at), - NewComments => pq.then_order_by(key::newest_comment_time_at), + NewComments => pq.then_order_by(CoalesceKey(key::newest_comment_time_at, key::published_at)), MostComments => pq.then_order_by(key::comments), Top => pq.then_order_by(key::score), }; @@ -971,16 +972,16 @@ mod tests { let pool = &data.pool(); let pool = &mut pool.into(); - let post_like_form = PostLikeForm::new(data.post.id, data.tegan.person.id, 1); + let post_like_form = PostLikeForm::new(data.post.id, data.tegan.person.id, true); let inserted_post_like = PostActions::like(pool, &post_like_form).await?; assert_eq!( - (data.post.id, data.tegan.person.id, Some(1)), + (data.post.id, data.tegan.person.id, Some(true)), ( inserted_post_like.post_id, inserted_post_like.person_id, - inserted_post_like.like_score, + inserted_post_like.vote_is_upvote, ) ); @@ -998,7 +999,7 @@ mod tests { ( post_listing_single_with_person .post_actions - .is_some_and(|t| t.like_score == Some(1)), + .is_some_and(|t| t.vote_is_upvote == Some(true)), // Make sure person actions is none so you don't get a voted_at for your own user post_listing_single_with_person.person_actions.is_none(), post_listing_single_with_person.post.score, @@ -1094,20 +1095,20 @@ mod tests { ); let bot_post_2 = Post::create(pool, &bot_post_2).await?; - let post_like_form = PostLikeForm::new(data.bot_post.id, data.tegan.person.id, 1); + let post_like_form = PostLikeForm::new(data.bot_post.id, data.tegan.person.id, true); let inserted_post_like = PostActions::like(pool, &post_like_form).await?; assert_eq!( - (data.bot_post.id, data.tegan.person.id, Some(1)), + (data.bot_post.id, data.tegan.person.id, Some(true)), ( inserted_post_like.post_id, inserted_post_like.person_id, - inserted_post_like.like_score, + inserted_post_like.vote_is_upvote, ) ); let inserted_person_like = - PersonActions::like(pool, data.tegan.person.id, data.bot.person.id, 1).await?; + PersonActions::like(pool, data.tegan.person.id, data.bot.person.id, true).await?; assert_eq!( (data.tegan.person.id, data.bot.person.id, Some(1), Some(0),), @@ -1133,7 +1134,7 @@ mod tests { ( post_listing .post_actions - .is_some_and(|t| t.like_score == Some(1)), + .is_some_and(|t| t.vote_is_upvote == Some(true)), post_listing .person_actions .as_ref() @@ -1149,10 +1150,10 @@ mod tests { ); // Do a 2nd like to another post - let post_2_like_form = PostLikeForm::new(bot_post_2.id, data.tegan.person.id, 1); + let post_2_like_form = PostLikeForm::new(bot_post_2.id, data.tegan.person.id, true); let _inserted_post_2_like = PostActions::like(pool, &post_2_like_form).await?; let inserted_person_like_2 = - PersonActions::like(pool, data.tegan.person.id, data.bot.person.id, 1).await?; + PersonActions::like(pool, data.tegan.person.id, data.bot.person.id, true).await?; assert_eq!( (data.tegan.person.id, data.bot.person.id, Some(2), Some(0),), ( @@ -1169,7 +1170,7 @@ mod tests { assert_eq!(UpleteCount::only_deleted(1), like_removed); let person_like_removed = - PersonActions::remove_like(pool, data.tegan.person.id, data.bot.person.id, 1).await?; + PersonActions::remove_like(pool, data.tegan.person.id, data.bot.person.id, true).await?; assert_eq!( (data.tegan.person.id, data.bot.person.id, Some(1), Some(0),), ( @@ -1181,10 +1182,10 @@ mod tests { ); // Now do a downvote - let post_like_form = PostLikeForm::new(data.bot_post.id, data.tegan.person.id, -1); + let post_like_form = PostLikeForm::new(data.bot_post.id, data.tegan.person.id, false); let _inserted_post_dislike = PostActions::like(pool, &post_like_form).await?; let inserted_person_dislike = - PersonActions::like(pool, data.tegan.person.id, data.bot.person.id, -1).await?; + PersonActions::like(pool, data.tegan.person.id, data.bot.person.id, false).await?; assert_eq!( (data.tegan.person.id, data.bot.person.id, Some(1), Some(1),), ( @@ -1209,7 +1210,7 @@ mod tests { ( post_listing .post_actions - .is_some_and(|t| t.like_score == Some(-1)), + .is_some_and(|t| t.vote_is_upvote == Some(false)), post_listing .person_actions .as_ref() diff --git a/crates/db_views/search_combined/src/impls.rs b/crates/db_views/search_combined/src/impls.rs index bea4c6293..7d683d2a6 100644 --- a/crates/db_views/search_combined/src/impls.rs +++ b/crates/db_views/search_combined/src/impls.rs @@ -290,25 +290,25 @@ impl SearchCombinedQuery { // Liked / disliked filter if let Some(my_id) = my_person_id { let not_creator_filter = item_creator.ne(my_id); - let liked_disliked_filter = |score: i16| { + let liked_disliked_filter = |should_be_upvote: bool| { search_combined::post_id .is_not_null() - .and(post_actions::like_score.eq(score)) + .and(post_actions::vote_is_upvote.eq(should_be_upvote)) .or( search_combined::comment_id .is_not_null() - .and(comment_actions::like_score.eq(score)), + .and(comment_actions::vote_is_upvote.eq(should_be_upvote)), ) }; if self.liked_only.unwrap_or_default() { query = query .filter(not_creator_filter) - .filter(liked_disliked_filter(1)); + .filter(liked_disliked_filter(true)); } else if self.disliked_only.unwrap_or_default() { query = query .filter(not_creator_filter) - .filter(liked_disliked_filter(-1)); + .filter(liked_disliked_filter(false)); } }; @@ -609,22 +609,22 @@ mod tests { let comment_in_nsfw_post = Comment::create(pool, &comment_in_nsfw_post_form, None).await?; // Timmy likes and dislikes a few things - let timmy_like_post_form = PostLikeForm::new(timmy_post.id, timmy.id, 1); + let timmy_like_post_form = PostLikeForm::new(timmy_post.id, timmy.id, true); PostActions::like(pool, &timmy_like_post_form).await?; - let timmy_like_sara_post_form = PostLikeForm::new(sara_post.id, timmy.id, 1); + let timmy_like_sara_post_form = PostLikeForm::new(sara_post.id, timmy.id, true); PostActions::like(pool, &timmy_like_sara_post_form).await?; - let timmy_dislike_post_form = PostLikeForm::new(timmy_post_2.id, timmy.id, -1); + let timmy_dislike_post_form = PostLikeForm::new(timmy_post_2.id, timmy.id, false); PostActions::like(pool, &timmy_dislike_post_form).await?; - let timmy_like_comment_form = CommentLikeForm::new(timmy.id, timmy_comment.id, 1); + let timmy_like_comment_form = CommentLikeForm::new(timmy.id, timmy_comment.id, true); CommentActions::like(pool, &timmy_like_comment_form).await?; - let timmy_like_sara_comment_form = CommentLikeForm::new(timmy.id, sara_comment.id, 1); + let timmy_like_sara_comment_form = CommentLikeForm::new(timmy.id, sara_comment.id, true); CommentActions::like(pool, &timmy_like_sara_comment_form).await?; - let timmy_dislike_sara_comment_form = CommentLikeForm::new(timmy.id, sara_comment_2.id, -1); + let timmy_dislike_sara_comment_form = CommentLikeForm::new(timmy.id, sara_comment_2.id, false); CommentActions::like(pool, &timmy_dislike_sara_comment_form).await?; Ok(Data { diff --git a/crates/db_views/vote/src/impls.rs b/crates/db_views/vote/src/impls.rs index 3790bc153..080a1bb6a 100644 --- a/crates/db_views/vote/src/impls.rs +++ b/crates/db_views/vote/src/impls.rs @@ -85,7 +85,7 @@ impl VoteView { .left_join(creator_home_instance_actions_join()) .left_join(creator_local_instance_actions_join) .filter(post_actions::post_id.eq(post_id)) - .filter(post_actions::like_score.is_not_null()) + .filter(post_actions::vote_is_upvote.is_not_null()) .select(( person::all_columns, creator_local_home_banned(), @@ -93,16 +93,16 @@ impl VoteView { .field(community_actions::received_ban_at) .nullable() .is_not_null(), - post_actions::like_score.assume_not_null(), + post_actions::vote_is_upvote.assume_not_null(), )) .limit(limit) .into_boxed(); // Sorting by like score let paginated_query = paginate(query, SortDirection::Asc, cursor_data, None, page_back) - .then_order_by(key::like_score) + .then_order_by(key::vote_is_upvote) // Tie breaker - .then_order_by(key::liked_at); + .then_order_by(key::voted_at); paginated_query .load::(conn) @@ -159,7 +159,7 @@ impl VoteView { .left_join(creator_home_instance_actions_join()) .left_join(creator_local_instance_actions_join) .filter(comment_actions::comment_id.eq(comment_id)) - .filter(comment_actions::like_score.is_not_null()) + .filter(comment_actions::vote_is_upvote.is_not_null()) .select(( person::all_columns, creator_local_home_banned(), @@ -167,16 +167,16 @@ impl VoteView { .field(community_actions::received_ban_at) .nullable() .is_not_null(), - comment_actions::like_score.assume_not_null(), + comment_actions::vote_is_upvote.assume_not_null(), )) .limit(limit) .into_boxed(); // Sorting by like score let paginated_query = paginate(query, SortDirection::Asc, cursor_data, None, page_back) - .then_order_by(key::like_score) + .then_order_by(key::vote_is_upvote) // Tie breaker - .then_order_by(key::liked_at); + .then_order_by(key::voted_at); paginated_query .load::(conn) @@ -243,11 +243,11 @@ mod tests { let inserted_comment = Comment::create(pool, &comment_form, None).await?; // Timmy upvotes his own post - let timmy_post_vote_form = PostLikeForm::new(inserted_post.id, inserted_timmy.id, 1); + let timmy_post_vote_form = PostLikeForm::new(inserted_post.id, inserted_timmy.id, true); PostActions::like(pool, &timmy_post_vote_form).await?; // Sara downvotes timmy's post - let sara_post_vote_form = PostLikeForm::new(inserted_post.id, inserted_sara.id, -1); + let sara_post_vote_form = PostLikeForm::new(inserted_post.id, inserted_sara.id, false); PostActions::like(pool, &sara_post_vote_form).await?; let mut expected_post_vote_views = [ @@ -255,13 +255,13 @@ mod tests { creator: inserted_sara.clone(), creator_banned: false, creator_banned_from_community: false, - score: -1, + is_upvote: false, }, VoteView { creator: inserted_timmy.clone(), creator_banned: false, creator_banned_from_community: false, - score: 1, + is_upvote: true, }, ]; expected_post_vote_views[1].creator.post_count = 1; @@ -272,11 +272,12 @@ mod tests { assert_eq!(read_post_vote_views, expected_post_vote_views); // Timothy votes down his own comment - let timmy_comment_vote_form = CommentLikeForm::new(inserted_timmy.id, inserted_comment.id, -1); + let timmy_comment_vote_form = + CommentLikeForm::new(inserted_timmy.id, inserted_comment.id, false); CommentActions::like(pool, &timmy_comment_vote_form).await?; // Sara upvotes timmy's comment - let sara_comment_vote_form = CommentLikeForm::new(inserted_sara.id, inserted_comment.id, 1); + let sara_comment_vote_form = CommentLikeForm::new(inserted_sara.id, inserted_comment.id, true); CommentActions::like(pool, &sara_comment_vote_form).await?; let mut expected_comment_vote_views = [ @@ -284,13 +285,13 @@ mod tests { creator: inserted_timmy.clone(), creator_banned: false, creator_banned_from_community: false, - score: -1, + is_upvote: false, }, VoteView { creator: inserted_sara.clone(), creator_banned: false, creator_banned_from_community: false, - score: 1, + is_upvote: true, }, ]; expected_comment_vote_views[0].creator.post_count = 1; diff --git a/crates/db_views/vote/src/lib.rs b/crates/db_views/vote/src/lib.rs index 54c2308dd..0ea40d114 100644 --- a/crates/db_views/vote/src/lib.rs +++ b/crates/db_views/vote/src/lib.rs @@ -18,5 +18,5 @@ pub struct VoteView { pub creator: Person, pub creator_banned: bool, pub creator_banned_from_community: bool, - pub score: i16, + pub is_upvote: bool, } diff --git a/crates/routes/src/utils/scheduled_tasks.rs b/crates/routes/src/utils/scheduled_tasks.rs index 157b14ee4..05f0c9b72 100644 --- a/crates/routes/src/utils/scheduled_tasks.rs +++ b/crates/routes/src/utils/scheduled_tasks.rs @@ -12,6 +12,7 @@ use diesel::{ NullableExpressionMethods, QueryDsl, QueryableByName, + SelectableHelper, }; use diesel_async::{AsyncPgConnection, RunQueryDsl}; use diesel_uplete::uplete; @@ -250,7 +251,7 @@ async fn process_post_aggregates_ranks_in_batches(conn: &mut AsyncPgConnection) FOR UPDATE SKIP LOCKED) UPDATE post pa SET hot_rank = r.hot_rank(pa.score, pa.published_at), - hot_rank_active = r.hot_rank(pa.score, pa.newest_comment_time_necro_at), + hot_rank_active = r.hot_rank(pa.score, coalesce(pa.newest_comment_time_necro_at, pa.published_at)), scaled_rank = r.scaled_rank(pa.score, pa.published_at, ca.interactions_month) FROM batch, community ca WHERE pa.id = batch.id @@ -593,7 +594,7 @@ async fn publish_scheduled_posts(context: &Data) -> LemmyResult<() .filter(not(exists(not_community_banned_action))) // ensure that user isnt banned from local .filter(not(exists(not_local_banned_action))) - .select((post::all_columns, community::all_columns)) + .select((Post::as_select(), Community::as_select())) .get_results::<(Post, Community)>(conn) .await?; @@ -771,7 +772,7 @@ mod tests { &PostInsertForm::new("i am grrreat".to_owned(), person.id, community.id), ) .await?; - PostActions::like(pool, &PostLikeForm::new(post.id, person.id, 1)).await?; + PostActions::like(pool, &PostLikeForm::new(post.id, person.id, true)).await?; active_counts(pool, ONE_DAY).await?; all_active_counts(pool).await?; diff --git a/migrations/2025-08-01-000016_smoosh-tables-together/down.sql b/migrations/2025-08-01-000016_smoosh-tables-together/down.sql index 95c4cbb22..8c6886aa1 100644 --- a/migrations/2025-08-01-000016_smoosh-tables-together/down.sql +++ b/migrations/2025-08-01-000016_smoosh-tables-together/down.sql @@ -134,7 +134,11 @@ INSERT INTO post_like (post_id, person_id, score, published) SELECT post_id, person_id, - like_score, + CASE WHEN vote_is_upvote THEN + 1 + ELSE + -1 + END, liked FROM post_actions @@ -186,7 +190,11 @@ INSERT INTO comment_like (comment_id, person_id, score, published) SELECT comment_id, person_id, - like_score, + CASE WHEN vote_is_upvote THEN + 1 + ELSE + -1 + END, liked FROM comment_actions diff --git a/migrations/2025-08-01-000016_smoosh-tables-together/up.sql b/migrations/2025-08-01-000016_smoosh-tables-together/up.sql index da5cc5a88..b6cd44284 100644 --- a/migrations/2025-08-01-000016_smoosh-tables-together/up.sql +++ b/migrations/2025-08-01-000016_smoosh-tables-together/up.sql @@ -5,11 +5,11 @@ -- comment_actions CREATE TABLE comment_actions AS SELECT + max(liked) AS liked, + max(saved) AS saved, person_id, comment_id, - cast(max(like_score) AS smallint) AS like_score, - max(liked) AS liked, - max(saved) AS saved + max(like_score) = 1 AS vote_is_upvote -- `null = 1` returns null FROM ( SELECT person_id, @@ -42,17 +42,17 @@ ALTER TABLE comment_actions ADD PRIMARY KEY (person_id, comment_id), ADD CONSTRAINT comment_actions_person_id_fkey FOREIGN KEY (person_id) REFERENCES person ON UPDATE CASCADE ON DELETE CASCADE, ADD CONSTRAINT comment_actions_comment_id_fkey FOREIGN KEY (comment_id) REFERENCES COMMENT ON UPDATE CASCADE ON DELETE CASCADE, - ADD CONSTRAINT comment_actions_check_liked CHECK (((liked IS NULL) = (like_score IS NULL))); + ADD CONSTRAINT comment_actions_check_liked CHECK (((liked IS NULL) = (vote_is_upvote IS NULL))); -- Create new indexes, with `OR` being used to allow `IS NOT NULL` filters in queries to use either column in --- a group (e.g. `liked IS NOT NULL` and `like_score IS NOT NULL` both work) +-- a group (e.g. `liked IS NOT NULL` and `vote_is_upvote IS NOT NULL` both work) CREATE INDEX idx_comment_actions_person ON comment_actions (person_id); CREATE INDEX idx_comment_actions_comment ON comment_actions (comment_id); CREATE INDEX idx_comment_actions_liked_not_null ON comment_actions (person_id, comment_id) WHERE - liked IS NOT NULL OR like_score IS NOT NULL; + liked IS NOT NULL OR vote_is_upvote IS NOT NULL; CREATE INDEX idx_comment_actions_saved_not_null ON comment_actions (person_id, comment_id) WHERE @@ -63,15 +63,15 @@ WHERE -- SO link on merges: https://stackoverflow.com/a/74066614/1655478 CREATE TABLE post_actions AS SELECT - person_id, - post_id, max(read) AS read, max(read_comments) AS read_comments, - cast(max(read_comments_amount) AS int) AS read_comments_amount, max(saved) AS saved, max(liked) AS liked, - cast(max(like_score) AS smallint) AS like_score, - max(hidden) AS hidden + max(hidden) AS hidden, + person_id, + post_id, + cast(max(read_comments_amount) AS int) AS read_comments_amount, + max(like_score) = 1 AS vote_is_upvote -- `null = 1` returns null FROM ( SELECT person_id, @@ -151,7 +151,7 @@ ALTER TABLE post_actions ADD PRIMARY KEY (person_id, post_id), ADD CONSTRAINT post_actions_person_id_fkey FOREIGN KEY (person_id) REFERENCES person ON UPDATE CASCADE ON DELETE CASCADE, ADD CONSTRAINT post_actions_post_id_fkey FOREIGN KEY (post_id) REFERENCES post ON UPDATE CASCADE ON DELETE CASCADE, - ADD CONSTRAINT post_actions_check_liked CHECK (((liked IS NULL) = (like_score IS NULL))), + ADD CONSTRAINT post_actions_check_liked CHECK (((liked IS NULL) = (vote_is_upvote IS NULL))), ADD CONSTRAINT post_actions_check_read_comments CHECK (((read_comments IS NULL) = (read_comments_amount IS NULL))); -- Create indexes @@ -173,7 +173,7 @@ WHERE CREATE INDEX idx_post_actions_liked_not_null ON post_actions (person_id, post_id) WHERE - liked IS NOT NULL OR like_score IS NOT NULL; + liked IS NOT NULL OR vote_is_upvote IS NOT NULL; CREATE INDEX idx_post_actions_hidden_not_null ON post_actions (person_id, post_id) WHERE @@ -182,15 +182,15 @@ WHERE -- community_actions CREATE TABLE community_actions AS SELECT - person_id, - community_id, max(followed) AS followed, - max(follow_state) AS follow_state, - max(follow_approver_id) AS follow_approver_id, max(blocked) AS blocked, max(became_moderator) AS became_moderator, max(received_ban) AS received_ban, - max(ban_expires) AS ban_expires + max(ban_expires) AS ban_expires, + person_id, + community_id, + max(follow_state) AS follow_state, + max(follow_approver_id) AS follow_approver_id FROM ( SELECT person_id, @@ -293,9 +293,9 @@ WHERE -- instance_actions CREATE TABLE instance_actions AS SELECT + published AS blocked, person_id, - instance_id, - published AS blocked + instance_id FROM instance_block; @@ -322,11 +322,11 @@ WHERE -- person_actions CREATE TABLE person_actions AS SELECT + max(followed) AS followed, + max(blocked) AS blocked, person_id, target_id, - max(followed) AS followed, - cast(max(follow_pending) AS boolean) AS follow_pending, - max(blocked) AS blocked + cast(max(follow_pending) AS boolean) AS follow_pending FROM ( SELECT follower_id AS person_id, @@ -376,7 +376,7 @@ WHERE -- `(liked, like_score)`, the query planner might othewise assume that `(TRUE, FALSE)` and `(TRUE, TRUE)` -- are equally likely when only `(TRUE, TRUE)` is possible, which would make it severely underestimate -- the efficiency of using the index) -CREATE statistics comment_actions_liked_stat ON (liked IS NULL), (like_score IS NULL) +CREATE statistics comment_actions_liked_stat ON (liked IS NULL), (vote_is_upvote IS NULL) FROM comment_actions; CREATE statistics community_actions_followed_stat ON (followed IS NULL), (follow_state IS NULL) @@ -388,6 +388,6 @@ FROM person_actions; CREATE statistics post_actions_read_comments_stat ON (read_comments IS NULL), (read_comments_amount IS NULL) FROM post_actions; -CREATE statistics post_actions_liked_stat ON (liked IS NULL), (like_score IS NULL), (post_id IS NULL) +CREATE statistics post_actions_liked_stat ON (liked IS NULL), (vote_is_upvote IS NULL), (post_id IS NULL) FROM post_actions; diff --git a/migrations/2025-08-01-000041_remove-aggregate-tables/down.sql b/migrations/2025-08-01-000041_remove-aggregate-tables/down.sql index 00ee1e737..eafc20683 100644 --- a/migrations/2025-08-01-000041_remove-aggregate-tables/down.sql +++ b/migrations/2025-08-01-000041_remove-aggregate-tables/down.sql @@ -93,8 +93,8 @@ SELECT upvotes, downvotes, published, - newest_comment_time_necro, - newest_comment_time, + coalesce(newest_comment_time_necro, published), + coalesce(newest_comment_time, published), featured_community, featured_local, hot_rank, @@ -102,7 +102,13 @@ SELECT community_id, creator_id, controversy_rank, - instance_id, + ( + SELECT + community.instance_id + FROM + community + WHERE + community.id = post.community_id) AS instance_id, scaled_rank, report_count, unresolved_report_count @@ -139,7 +145,6 @@ ALTER TABLE post DROP COLUMN hot_rank, DROP COLUMN hot_rank_active, DROP COLUMN controversy_rank, - DROP COLUMN instance_id, DROP COLUMN scaled_rank, DROP COLUMN report_count, DROP COLUMN unresolved_report_count; diff --git a/migrations/2025-08-01-000041_remove-aggregate-tables/up.sql b/migrations/2025-08-01-000041_remove-aggregate-tables/up.sql index 509b4ac7a..c67d9a2e5 100644 --- a/migrations/2025-08-01-000041_remove-aggregate-tables/up.sql +++ b/migrations/2025-08-01-000041_remove-aggregate-tables/up.sql @@ -1,14 +1,20 @@ -- Merge comment_aggregates into comment table ALTER TABLE comment - ADD COLUMN score int NOT NULL DEFAULT 0, - ADD COLUMN upvotes int NOT NULL DEFAULT 0, + ADD COLUMN score int NOT NULL DEFAULT 1, -- Default value only for previous rows, to match the similar thing done with `upvotes` + ADD COLUMN upvotes int NOT NULL DEFAULT 1, -- Default value only for previous rows, so the update below can filter out more rows by using `upvotes != 1` instead of `upvotes != 0` ADD COLUMN downvotes int NOT NULL DEFAULT 0, ADD COLUMN child_count int NOT NULL DEFAULT 0, - ADD COLUMN hot_rank double precision NOT NULL DEFAULT 0.0001, - ADD COLUMN controversy_rank double precision NOT NULL DEFAULT 0, + ADD COLUMN hot_rank real NOT NULL DEFAULT 0, -- Default value only for previous rows, so the update below can filter out more rows by using `hot_rank != 0` instead of `hot_rank != 0.0001` + ADD COLUMN controversy_rank real NOT NULL DEFAULT 0, ADD COLUMN report_count smallint NOT NULL DEFAULT 0, ADD COLUMN unresolved_report_count smallint NOT NULL DEFAULT 0; +-- Default values only for future rows +ALTER TABLE comment + ALTER COLUMN score SET DEFAULT 0, + ALTER COLUMN upvotes SET DEFAULT 0, + ALTER COLUMN hot_rank SET DEFAULT 0.0001; + -- Disable the triggers temporarily ALTER TABLE comment DISABLE TRIGGER ALL; @@ -40,7 +46,14 @@ SET FROM comment_aggregates AS ca WHERE - comment.id = ca.comment_id; + comment.id = ca.comment_id + -- If `(upvotes, downvotes) = (1, 0)`, then `(score, controversy_rank) = (1, 0)`, so it would be redundant to check `score` and `controversy_rank` in this filter. + AND (ca.upvotes != 1 + OR ca.downvotes != 0 + OR ca.child_count != 0 + OR ca.hot_rank != 0 + OR ca.report_count != 0 + OR ca.unresolved_report_count != 0); DROP TABLE comment_aggregates; @@ -77,20 +90,27 @@ CREATE INDEX idx_comment_score ON comment USING btree (score DESC); -- merge post_aggregates into post table ALTER TABLE post + ADD COLUMN newest_comment_time_necro timestamp with time zone, + ADD COLUMN newest_comment_time timestamp with time zone, ADD COLUMN comments int NOT NULL DEFAULT 0, - ADD COLUMN score int NOT NULL DEFAULT 0, - ADD COLUMN upvotes int NOT NULL DEFAULT 0, + ADD COLUMN score int NOT NULL DEFAULT 1, -- Default value only for previous rows, to match the similar thing done with `upvotes` + ADD COLUMN upvotes int NOT NULL DEFAULT 1, -- Default value only for previous rows, so the update below can filter out more rows by using `upvotes != 1` instead of `upvotes != 0` ADD COLUMN downvotes int NOT NULL DEFAULT 0, - ADD COLUMN newest_comment_time_necro timestamp with time zone NOT NULL DEFAULT now(), - ADD COLUMN newest_comment_time timestamp with time zone NOT NULL DEFAULT now(), - ADD COLUMN hot_rank double precision NOT NULL DEFAULT 0.0001, - ADD COLUMN hot_rank_active double precision NOT NULL DEFAULT 0.0001, - ADD COLUMN controversy_rank double precision NOT NULL DEFAULT 0, - ADD COLUMN instance_id int REFERENCES instance (id) ON UPDATE CASCADE ON DELETE CASCADE, - ADD COLUMN scaled_rank double precision NOT NULL DEFAULT 0.0001, + ADD COLUMN hot_rank real NOT NULL DEFAULT 0, -- Default value only for previous rows, so the update below can filter out more rows by using `hot_rank != 0` instead of `hot_rank != 0.0001` + ADD COLUMN hot_rank_active real NOT NULL DEFAULT 0, -- Default value only for previous rows, so the update below can filter out more rows by using `hot_rank_active != 0` instead of `hot_rank_active != 0.0001` + ADD COLUMN controversy_rank real NOT NULL DEFAULT 0, + ADD COLUMN scaled_rank real NOT NULL DEFAULT 0, -- Default value only for previous rows, so the update below can filter out more rows by using `scaled_rank != 0` instead of `scaled_rank != 0.0001` ADD COLUMN report_count smallint NOT NULL DEFAULT 0, ADD COLUMN unresolved_report_count smallint NOT NULL DEFAULT 0; +-- Default values only for future rows +ALTER TABLE post + ALTER COLUMN score SET DEFAULT 0, + ALTER COLUMN upvotes SET DEFAULT 0, + ALTER COLUMN hot_rank SET DEFAULT 0.0001, + ALTER COLUMN hot_rank_active SET DEFAULT 0.0001, + ALTER COLUMN scaled_rank SET DEFAULT 0.0001; + -- Disable the triggers temporarily ALTER TABLE post DISABLE TRIGGER ALL; @@ -111,23 +131,33 @@ WHERE UPDATE post SET + newest_comment_time_necro = nullif (pa.newest_comment_time_necro, pa.published), + newest_comment_time = nullif (pa.newest_comment_time, pa.published), comments = pa.comments, score = pa.score, upvotes = pa.upvotes, downvotes = pa.downvotes, - newest_comment_time_necro = pa.newest_comment_time_necro, - newest_comment_time = pa.newest_comment_time, hot_rank = pa.hot_rank, hot_rank_active = pa.hot_rank_active, controversy_rank = pa.controversy_rank, - instance_id = pa.instance_id, scaled_rank = pa.scaled_rank, report_count = pa.report_count, unresolved_report_count = pa.unresolved_report_count FROM post_aggregates AS pa WHERE - post.id = pa.post_id; + post.id = pa.post_id + -- If `(upvotes, downvotes) = (1, 0)`, then `(score, controversy_rank) = (1, 0)`, so it would be redundant to check `score` and `controversy_rank` in this filter. + AND (pa.newest_comment_time_necro != pa.published + OR pa.newest_comment_time != pa.published + OR pa.comments != 0 + OR pa.upvotes != 1 + OR pa.downvotes != 0 + OR pa.hot_rank != 0 + OR pa.hot_rank_active != 0 + OR pa.scaled_rank != 0 + OR pa.report_count != 0 + OR pa.unresolved_report_count != 0); -- Delete that data DROP TABLE post_aggregates; @@ -160,9 +190,9 @@ CREATE INDEX idx_post_community_hot ON post USING btree (community_id, featured_ CREATE INDEX idx_post_community_most_comments ON post USING btree (community_id, featured_local DESC, comments DESC, published DESC, id DESC); -CREATE INDEX idx_post_community_newest_comment_time ON post USING btree (community_id, featured_local DESC, newest_comment_time DESC, id DESC); +CREATE INDEX idx_post_community_newest_comment_time ON post USING btree (community_id, featured_local DESC, coalesce(newest_comment_time, published) DESC, id DESC); -CREATE INDEX idx_post_community_newest_comment_time_necro ON post USING btree (community_id, featured_local DESC, newest_comment_time_necro DESC, id DESC); +CREATE INDEX idx_post_community_newest_comment_time_necro ON post USING btree (community_id, featured_local DESC, coalesce(newest_comment_time_necro, published) DESC, id DESC); -- INDEX idx_post_community_published ON post USING btree (community_id, featured_local DESC, published DESC); --CREATE INDEX idx_post_community_published_asc ON post USING btree (community_id, featured_local DESC, reverse_timestamp_sort (published) DESC); @@ -178,9 +208,9 @@ CREATE INDEX idx_post_featured_community_hot ON post USING btree (community_id, CREATE INDEX idx_post_featured_community_most_comments ON post USING btree (community_id, featured_community DESC, comments DESC, published DESC, id DESC); -CREATE INDEX idx_post_featured_community_newest_comment_time ON post USING btree (community_id, featured_community DESC, newest_comment_time DESC, id DESC); +CREATE INDEX idx_post_featured_community_newest_comment_time ON post USING btree (community_id, featured_community DESC, coalesce(newest_comment_time, published) DESC, id DESC); -CREATE INDEX idx_post_featured_community_newest_comment_time_necr ON post USING btree (community_id, featured_community DESC, newest_comment_time_necro DESC, id DESC); +CREATE INDEX idx_post_featured_community_newest_comment_time_necr ON post USING btree (community_id, featured_community DESC, coalesce(newest_comment_time_necro, published) DESC, id DESC); --CREATE INDEX idx_post_featured_community_published ON post USING btree (community_id, featured_community DESC, published DESC); CREATE INDEX idx_post_featured_community_published_asc ON post USING btree (community_id, featured_community DESC, reverse_timestamp_sort (published) DESC, id DESC); @@ -197,9 +227,9 @@ CREATE INDEX idx_post_featured_local_hot ON post USING btree (featured_local DES CREATE INDEX idx_post_featured_local_most_comments ON post USING btree (featured_local DESC, comments DESC, published DESC, id DESC); -CREATE INDEX idx_post_featured_local_newest_comment_time ON post USING btree (featured_local DESC, newest_comment_time DESC, id DESC); +CREATE INDEX idx_post_featured_local_newest_comment_time ON post USING btree (featured_local DESC, coalesce(newest_comment_time, published) DESC, id DESC); -CREATE INDEX idx_post_featured_local_newest_comment_time_necro ON post USING btree (featured_local DESC, newest_comment_time_necro DESC, id DESC); +CREATE INDEX idx_post_featured_local_newest_comment_time_necro ON post USING btree (featured_local DESC, coalesce(newest_comment_time_necro, published) DESC, id DESC); CREATE INDEX idx_post_featured_local_published ON post USING btree (featured_local DESC, published DESC, id DESC); @@ -216,18 +246,23 @@ CREATE INDEX idx_post_published_asc ON post USING btree (reverse_timestamp_sort -- merge community_aggregates into community table ALTER TABLE community - ADD COLUMN subscribers int NOT NULL DEFAULT 0, + ADD COLUMN subscribers int NOT NULL DEFAULT 1, -- Default value only for previous rows, so the update below can filter out more rows by using `subscribers != 1` instead of `subscribers != 0` ADD COLUMN posts int NOT NULL DEFAULT 0, ADD COLUMN comments int NOT NULL DEFAULT 0, ADD COLUMN users_active_day int NOT NULL DEFAULT 0, ADD COLUMN users_active_week int NOT NULL DEFAULT 0, ADD COLUMN users_active_month int NOT NULL DEFAULT 0, ADD COLUMN users_active_half_year int NOT NULL DEFAULT 0, - ADD COLUMN hot_rank double precision NOT NULL DEFAULT 0.0001, + ADD COLUMN hot_rank real NOT NULL DEFAULT 0, -- Default value only for previous rows, so the update below can filter out more rows by using `hot_rank != 0` instead of `hot_rank != 0.0001` ADD COLUMN subscribers_local int NOT NULL DEFAULT 0, + ADD COLUMN interactions_month int NOT NULL DEFAULT 0, ADD COLUMN report_count smallint NOT NULL DEFAULT 0, - ADD COLUMN unresolved_report_count smallint NOT NULL DEFAULT 0, - ADD COLUMN interactions_month int NOT NULL DEFAULT 0; + ADD COLUMN unresolved_report_count smallint NOT NULL DEFAULT 0; + +-- Default values only for future rows +ALTER TABLE community + ALTER COLUMN subscribers SET DEFAULT 0, + ALTER COLUMN hot_rank SET DEFAULT 0.0001; -- Disable the triggers temporarily ALTER TABLE community DISABLE TRIGGER ALL; @@ -258,13 +293,25 @@ SET users_active_half_year = ca.users_active_half_year, hot_rank = ca.hot_rank, subscribers_local = ca.subscribers_local, + interactions_month = ca.interactions_month, report_count = ca.report_count, - unresolved_report_count = ca.unresolved_report_count, - interactions_month = ca.interactions_month + unresolved_report_count = ca.unresolved_report_count FROM community_aggregates AS ca WHERE - community.id = ca.community_id; + community.id = ca.community_id + AND (ca.subscribers != 1 + OR ca.posts != 0 + OR ca.comments != 0 + OR ca.users_active_day != 0 + OR ca.users_active_week != 0 + OR ca.users_active_month != 0 + OR ca.users_active_half_year != 0 + OR ca.hot_rank != 0 + OR ca.subscribers_local != 0 + OR ca.interactions_month != 0 + OR ca.report_count != 0 + OR ca.unresolved_report_count != 0); DROP TABLE community_aggregates; @@ -290,7 +337,7 @@ REINDEX TABLE community; CREATE INDEX idx_community_hot ON public.community USING btree (hot_rank DESC); -CREATE INDEX idx_community_nonzero_hotrank ON public.community USING btree (published) +CREATE INDEX idx_community_nonzero_hotrank ON community USING btree (published) WHERE (hot_rank <> (0)::double precision); CREATE INDEX idx_community_subscribers ON public.community USING btree (subscribers DESC); @@ -331,7 +378,11 @@ SET FROM person_aggregates AS pa WHERE - person.id = pa.person_id; + person.id = pa.person_id + AND (pa.post_count != 0 + OR pa.post_score != 0 + OR pa.comment_count != 0 + OR pa.comment_score != 0); DROP TABLE person_aggregates; @@ -455,7 +506,11 @@ SET FROM local_user_vote_display_mode AS v WHERE - local_user.id = v.local_user_id; + local_user.id = v.local_user_id + AND (v.score + OR NOT v.upvotes + OR NOT v.downvotes + OR v.upvote_percentage); DROP TABLE local_user_vote_display_mode; diff --git a/migrations/2025-08-01-000042_community-hidden-visibility/down.sql b/migrations/2025-08-01-000042_community-hidden-visibility/down.sql index f62cc8ec3..b6d256a28 100644 --- a/migrations/2025-08-01-000042_community-hidden-visibility/down.sql +++ b/migrations/2025-08-01-000042_community-hidden-visibility/down.sql @@ -1,132 +1,18 @@ -- recreate columns in the original order ALTER TABLE community ADD COLUMN hidden bool DEFAULT FALSE NOT NULL, - ADD COLUMN posting_restricted_to_mods_new bool NOT NULL DEFAULT FALSE, - ADD COLUMN instance_id_new int, - ADD COLUMN moderators_url_new varchar(255), - ADD COLUMN featured_url_new varchar(255), - ADD COLUMN visibility_new community_visibility NOT NULL DEFAULT 'Public', - ADD COLUMN description_new varchar(150), - ADD COLUMN random_number_new smallint NOT NULL DEFAULT random_smallint (), - ADD COLUMN subscribers_new int NOT NULL DEFAULT 0, - ADD COLUMN posts_new int NOT NULL DEFAULT 0, - ADD COLUMN comments_new int NOT NULL DEFAULT 0, - ADD COLUMN users_active_day_new int NOT NULL DEFAULT 0, - ADD COLUMN users_active_week_new int NOT NULL DEFAULT 0, - ADD COLUMN users_active_month_new int NOT NULL DEFAULT 0, - ADD COLUMN users_active_half_year_new int NOT NULL DEFAULT 0, - ADD COLUMN hot_rank_new double precision NOT NULL DEFAULT 0.0001, - ADD COLUMN subscribers_local_new int NOT NULL DEFAULT 0, - ADD COLUMN report_count_new smallint NOT NULL DEFAULT 0, - ADD COLUMN unresolved_report_count_new smallint NOT NULL DEFAULT 0, - ADD COLUMN interactions_month_new int NOT NULL DEFAULT 0; + ADD COLUMN visibility_new community_visibility NOT NULL DEFAULT 'Public'; UPDATE community SET - (posting_restricted_to_mods_new, - instance_id_new, - moderators_url_new, - featured_url_new, - visibility_new, - description_new, - random_number_new, - subscribers_new, - posts_new, - comments_new, - users_active_day_new, - users_active_week_new, - users_active_month_new, - users_active_half_year_new, - hot_rank_new, - subscribers_local_new, - report_count_new, - unresolved_report_count_new, - interactions_month_new) = (posting_restricted_to_mods, - instance_id, - moderators_url, - featured_url, - visibility, - description, - random_number, - subscribers, - posts, - comments, - users_active_day, - users_active_week, - users_active_month, - users_active_half_year, - hot_rank, - subscribers_local, - report_count, - unresolved_report_count, - interactions_month); + visibility_new = visibility; ALTER TABLE community - ALTER COLUMN instance_id_new SET NOT NULL, - DROP COLUMN posting_restricted_to_mods, - DROP COLUMN instance_id, - DROP COLUMN moderators_url, - DROP COLUMN featured_url, - DROP COLUMN visibility, - DROP COLUMN description, - DROP COLUMN random_number, - DROP COLUMN subscribers, - DROP COLUMN posts, - DROP COLUMN comments, - DROP COLUMN users_active_day, - DROP COLUMN users_active_week, - DROP COLUMN users_active_month, - DROP COLUMN users_active_half_year, - DROP COLUMN hot_rank, - DROP COLUMN subscribers_local, - DROP COLUMN report_count, - DROP COLUMN unresolved_report_count, - DROP COLUMN interactions_month; - -ALTER TABLE community RENAME COLUMN posting_restricted_to_mods_new TO posting_restricted_to_mods; - -ALTER TABLE community RENAME COLUMN instance_id_new TO instance_id; - -ALTER TABLE community RENAME COLUMN moderators_url_new TO moderators_url; - -ALTER TABLE community RENAME COLUMN featured_url_new TO featured_url; + DROP COLUMN visibility; ALTER TABLE community RENAME COLUMN visibility_new TO visibility; -ALTER TABLE community RENAME COLUMN description_new TO description; - -ALTER TABLE community RENAME COLUMN random_number_new TO random_number; - -ALTER TABLE community RENAME COLUMN subscribers_new TO subscribers; - -ALTER TABLE community RENAME COLUMN posts_new TO posts; - -ALTER TABLE community RENAME COLUMN comments_new TO comments; - -ALTER TABLE community RENAME COLUMN users_active_day_new TO users_active_day; - -ALTER TABLE community RENAME COLUMN users_active_week_new TO users_active_week; - -ALTER TABLE community RENAME COLUMN users_active_month_new TO users_active_month; - -ALTER TABLE community RENAME COLUMN users_active_half_year_new TO users_active_half_year; - -ALTER TABLE community RENAME COLUMN hot_rank_new TO hot_rank; - -ALTER TABLE community RENAME COLUMN subscribers_local_new TO subscribers_local; - -ALTER TABLE community RENAME COLUMN report_count_new TO report_count; - -ALTER TABLE community RENAME COLUMN unresolved_report_count_new TO unresolved_report_count; - -ALTER TABLE community RENAME COLUMN interactions_month_new TO interactions_month; - -ALTER TABLE community - ADD CONSTRAINT community_featured_url_key UNIQUE (featured_url), - ADD CONSTRAINT community_moderators_url_key UNIQUE (moderators_url), - ADD CONSTRAINT community_instance_id_fkey FOREIGN KEY (instance_id) REFERENCES instance (id) ON UPDATE CASCADE ON DELETE CASCADE; - -- same changes as up.sql, but the other way round UPDATE community @@ -161,15 +47,6 @@ CREATE INDEX idx_community_random_number ON community (random_number) INCLUDE (l WHERE NOT (deleted OR removed OR visibility = 'Private'); -CREATE INDEX idx_community_nonzero_hotrank ON community USING btree (published) -WHERE (hot_rank <> (0)::double precision); - -CREATE INDEX idx_community_subscribers ON community USING btree (subscribers DESC); - -CREATE INDEX idx_community_users_active_month ON community USING btree (users_active_month DESC); - -CREATE INDEX idx_community_hot ON public.community USING btree (hot_rank DESC); - REINDEX TABLE community; -- revert modlog table changes diff --git a/migrations/2025-08-01-000045_site_person_ban/down.sql b/migrations/2025-08-01-000045_site_person_ban/down.sql index 12daefb58..c3ecd426f 100644 --- a/migrations/2025-08-01-000045_site_person_ban/down.sql +++ b/migrations/2025-08-01-000045_site_person_ban/down.sql @@ -17,11 +17,7 @@ ALTER TABLE person ADD COLUMN matrix_user_id_new text, ADD COLUMN bot_account_new boolean DEFAULT FALSE NOT NULL, ADD COLUMN ban_expires timestamptz, - ADD COLUMN instance_id_new int, - ADD COLUMN post_count_new int DEFAULT 0 NOT NULL, - ADD COLUMN post_score_new int DEFAULT 0 NOT NULL, - ADD COLUMN comment_count_new int DEFAULT 0 NOT NULL, - ADD COLUMN comment_score_new int DEFAULT 0 NOT NULL; + ADD COLUMN instance_id_new int; UPDATE person @@ -39,11 +35,7 @@ SET inbox_url_new, matrix_user_id_new, bot_account_new, - instance_id_new, - post_count_new, - post_score_new, - comment_count_new, - comment_score_new) = (published, + instance_id_new) = (published, updated, ap_id, bio, @@ -56,11 +48,7 @@ SET inbox_url, matrix_user_id, bot_account, - instance_id, - post_count, - post_score, - comment_count, - comment_score); + instance_id); ALTER TABLE person DROP COLUMN published, @@ -76,11 +64,7 @@ ALTER TABLE person DROP COLUMN inbox_url, DROP COLUMN matrix_user_id, DROP COLUMN bot_account, - DROP COLUMN instance_id, - DROP COLUMN post_count, - DROP COLUMN post_score, - DROP COLUMN comment_count, - DROP COLUMN comment_score; + DROP COLUMN instance_id; ALTER TABLE person RENAME COLUMN published_new TO published; @@ -110,14 +94,6 @@ ALTER TABLE person RENAME COLUMN bot_account_new TO bot_account; ALTER TABLE person RENAME COLUMN instance_id_new TO instance_id; -ALTER TABLE person RENAME COLUMN post_count_new TO post_count; - -ALTER TABLE person RENAME COLUMN post_score_new TO post_score; - -ALTER TABLE person RENAME COLUMN comment_count_new TO comment_count; - -ALTER TABLE person RENAME COLUMN comment_score_new TO comment_score; - ALTER TABLE person ALTER public_key SET NOT NULL, ALTER instance_id SET NOT NULL, diff --git a/migrations/2025-08-01-000046_remove_post_instance_id/down.sql b/migrations/2025-08-01-000046_remove_post_instance_id/down.sql deleted file mode 100644 index 7e64da609..000000000 --- a/migrations/2025-08-01-000046_remove_post_instance_id/down.sql +++ /dev/null @@ -1,339 +0,0 @@ -ALTER TABLE post - ADD COLUMN name_new character varying(200), - ADD COLUMN url_new character varying(2000), - ADD COLUMN body_new text, - ADD COLUMN creator_id_new integer, - ADD COLUMN community_id_new integer, - ADD COLUMN removed_new boolean DEFAULT FALSE NOT NULL, - ADD COLUMN locked_new boolean DEFAULT FALSE NOT NULL, - ADD COLUMN published_new timestamp with time zone DEFAULT now() NOT NULL, - ADD COLUMN updated_new timestamp with time zone, - ADD COLUMN deleted_new boolean DEFAULT FALSE NOT NULL, - ADD COLUMN nsfw_new boolean DEFAULT FALSE NOT NULL, - ADD COLUMN embed_title_new text, - ADD COLUMN embed_description_new text, - ADD COLUMN thumbnail_url_new text, - ADD COLUMN ap_id_new character varying(255), - ADD COLUMN local_new boolean DEFAULT TRUE NOT NULL, - ADD COLUMN embed_video_url_new text, - ADD COLUMN language_id_new integer DEFAULT 0 NOT NULL, - ADD COLUMN featured_community_new boolean DEFAULT FALSE NOT NULL, - ADD COLUMN featured_local_new boolean DEFAULT FALSE NOT NULL, - ADD COLUMN url_content_type_new text, - ADD COLUMN alt_text_new text, - ADD COLUMN scheduled_publish_time_new timestamp with time zone, - ADD COLUMN comments_new int DEFAULT 0 NOT NULL, - ADD COLUMN score_new int DEFAULT 0 NOT NULL, - ADD COLUMN upvotes_new int DEFAULT 0 NOT NULL, - ADD COLUMN downvotes_new int DEFAULT 0 NOT NULL, - ADD COLUMN newest_comment_time_necro_new timestamp with time zone DEFAULT now() NOT NULL, - ADD COLUMN newest_comment_time_new timestamp with time zone DEFAULT now() NOT NULL, - ADD COLUMN hot_rank_new double precision DEFAULT 0.0001 NOT NULL, - ADD COLUMN hot_rank_active_new double precision DEFAULT 0.0001 NOT NULL, - ADD COLUMN controversy_rank_new double precision DEFAULT 0 NOT NULL, - -- Old column here - ADD COLUMN instance_id integer, - ADD COLUMN scaled_rank_new double precision DEFAULT 0.0001 NOT NULL, - ADD COLUMN report_count_new smallint DEFAULT 0 NOT NULL, - ADD COLUMN unresolved_report_count_new smallint DEFAULT 0 NOT NULL, - ADD COLUMN federation_pending_new boolean DEFAULT FALSE NOT NULL; - -UPDATE - post -SET - (instance_id, - name_new, - url_new, - body_new, - creator_id_new, - community_id_new, - removed_new, - locked_new, - published_new, - updated_new, - deleted_new, - nsfw_new, - embed_title_new, - embed_description_new, - thumbnail_url_new, - ap_id_new, - local_new, - embed_video_url_new, - language_id_new, - featured_community_new, - featured_local_new, - url_content_type_new, - alt_text_new, - scheduled_publish_time_new, - comments_new, - score_new, - upvotes_new, - downvotes_new, - newest_comment_time_necro_new, - newest_comment_time_new, - hot_rank_new, - hot_rank_active_new, - controversy_rank_new, - scaled_rank_new, - report_count_new, - unresolved_report_count_new, - federation_pending_new) = (0, - name, - url, - body, - creator_id, - community_id, - removed, - LOCKED, - published, - updated, - deleted, - nsfw, - embed_title, - embed_description, - thumbnail_url, - ap_id, - local, - embed_video_url, - language_id, - featured_community, - featured_local, - url_content_type, - alt_text, - scheduled_publish_time, - comments, - score, - upvotes, - downvotes, - newest_comment_time_necro, - newest_comment_time, - hot_rank, - hot_rank_active, - controversy_rank, - scaled_rank, - report_count, - unresolved_report_count, - federation_pending); - -ALTER TABLE post - DROP COLUMN name, - DROP COLUMN url, - DROP COLUMN body, - DROP COLUMN creator_id, - DROP COLUMN community_id, - DROP COLUMN removed, - DROP COLUMN LOCKED, - DROP COLUMN published, - DROP COLUMN updated, - DROP COLUMN deleted, - DROP COLUMN nsfw, - DROP COLUMN embed_title, - DROP COLUMN embed_description, - DROP COLUMN thumbnail_url, - DROP COLUMN ap_id, - DROP COLUMN local, - DROP COLUMN embed_video_url, - DROP COLUMN language_id, - DROP COLUMN featured_community, - DROP COLUMN featured_local, - DROP COLUMN url_content_type, - DROP COLUMN alt_text, - DROP COLUMN scheduled_publish_time, - DROP COLUMN comments, - DROP COLUMN score, - DROP COLUMN upvotes, - DROP COLUMN downvotes, - DROP COLUMN newest_comment_time_necro, - DROP COLUMN newest_comment_time, - DROP COLUMN hot_rank, - DROP COLUMN hot_rank_active, - DROP COLUMN controversy_rank, - DROP COLUMN scaled_rank, - DROP COLUMN report_count, - DROP COLUMN unresolved_report_count, - DROP COLUMN federation_pending; - -ALTER TABLE post RENAME COLUMN name_new TO name; - -ALTER TABLE post RENAME COLUMN url_new TO url; - -ALTER TABLE post RENAME COLUMN body_new TO body; - -ALTER TABLE post RENAME COLUMN creator_id_new TO creator_id; - -ALTER TABLE post RENAME COLUMN community_id_new TO community_id; - -ALTER TABLE post RENAME COLUMN removed_new TO removed; - -ALTER TABLE post RENAME COLUMN locked_new TO LOCKED; - -ALTER TABLE post RENAME COLUMN published_new TO published; - -ALTER TABLE post RENAME COLUMN updated_new TO updated; - -ALTER TABLE post RENAME COLUMN deleted_new TO deleted; - -ALTER TABLE post RENAME COLUMN nsfw_new TO nsfw; - -ALTER TABLE post RENAME COLUMN embed_title_new TO embed_title; - -ALTER TABLE post RENAME COLUMN embed_description_new TO embed_description; - -ALTER TABLE post RENAME COLUMN thumbnail_url_new TO thumbnail_url; - -ALTER TABLE post RENAME COLUMN ap_id_new TO ap_id; - -ALTER TABLE post RENAME COLUMN local_new TO local; - -ALTER TABLE post RENAME COLUMN embed_video_url_new TO embed_video_url; - -ALTER TABLE post RENAME COLUMN language_id_new TO language_id; - -ALTER TABLE post RENAME COLUMN featured_community_new TO featured_community; - -ALTER TABLE post RENAME COLUMN featured_local_new TO featured_local; - -ALTER TABLE post RENAME COLUMN url_content_type_new TO url_content_type; - -ALTER TABLE post RENAME COLUMN alt_text_new TO alt_text; - -ALTER TABLE post RENAME COLUMN scheduled_publish_time_new TO scheduled_publish_time; - -ALTER TABLE post RENAME COLUMN comments_new TO comments; - -ALTER TABLE post RENAME COLUMN score_new TO score; - -ALTER TABLE post RENAME COLUMN upvotes_new TO upvotes; - -ALTER TABLE post RENAME COLUMN downvotes_new TO downvotes; - -ALTER TABLE post RENAME COLUMN newest_comment_time_necro_new TO newest_comment_time_necro; - -ALTER TABLE post RENAME COLUMN newest_comment_time_new TO newest_comment_time; - -ALTER TABLE post RENAME COLUMN hot_rank_new TO hot_rank; - -ALTER TABLE post RENAME COLUMN hot_rank_active_new TO hot_rank_active; - -ALTER TABLE post RENAME COLUMN controversy_rank_new TO controversy_rank; - -ALTER TABLE post RENAME COLUMN scaled_rank_new TO scaled_rank; - -ALTER TABLE post RENAME COLUMN report_count_new TO report_count; - -ALTER TABLE post RENAME COLUMN unresolved_report_count_new TO unresolved_report_count; - -ALTER TABLE post RENAME COLUMN federation_pending_new TO federation_pending; - --- Update the historical instance_id rows -UPDATE - post AS p -SET - instance_id = c.instance_id -FROM - community AS c -WHERE - p.community_id = c.id; - -ALTER TABLE ONLY post - ADD CONSTRAINT idx_post_ap_id UNIQUE (ap_id); - -CREATE INDEX idx_post_community ON post USING btree (community_id); - -CREATE INDEX idx_post_community_active ON post USING btree (community_id, featured_local DESC, hot_rank_active DESC, published DESC, id DESC); - -CREATE INDEX idx_post_community_controversy ON post USING btree (community_id, featured_local DESC, controversy_rank DESC, id DESC); - -CREATE INDEX idx_post_community_hot ON post USING btree (community_id, featured_local DESC, hot_rank DESC, published DESC, id DESC); - -CREATE INDEX idx_post_community_most_comments ON post USING btree (community_id, featured_local DESC, comments DESC, published DESC, id DESC); - -CREATE INDEX idx_post_community_newest_comment_time ON post USING btree (community_id, featured_local DESC, newest_comment_time DESC, id DESC); - -CREATE INDEX idx_post_community_newest_comment_time_necro ON post USING btree (community_id, featured_local DESC, newest_comment_time_necro DESC, id DESC); - -CREATE INDEX idx_post_community_scaled ON post USING btree (community_id, featured_local DESC, scaled_rank DESC, published DESC, id DESC); - -CREATE INDEX idx_post_community_score ON post USING btree (community_id, featured_local DESC, score DESC, published DESC, id DESC); - -CREATE INDEX idx_post_creator ON post USING btree (creator_id); - -CREATE INDEX idx_post_featured_community_active ON post USING btree (community_id, featured_community DESC, hot_rank_active DESC, published DESC, id DESC); - -CREATE INDEX idx_post_featured_community_controversy ON post USING btree (community_id, featured_community DESC, controversy_rank DESC, id DESC); - -CREATE INDEX idx_post_featured_community_hot ON post USING btree (community_id, featured_community DESC, hot_rank DESC, published DESC, id DESC); - -CREATE INDEX idx_post_featured_community_most_comments ON post USING btree (community_id, featured_community DESC, comments DESC, published DESC, id DESC); - -CREATE INDEX idx_post_featured_community_newest_comment_time ON post USING btree (community_id, featured_community DESC, newest_comment_time DESC, id DESC); - -CREATE INDEX idx_post_featured_community_newest_comment_time_necr ON post USING btree (community_id, featured_community DESC, newest_comment_time_necro DESC, id DESC); - -CREATE INDEX idx_post_featured_community_published_asc ON post USING btree (community_id, featured_community DESC, reverse_timestamp_sort (published) DESC, id DESC); - -CREATE INDEX idx_post_featured_community_scaled ON post USING btree (community_id, featured_community DESC, scaled_rank DESC, published DESC, id DESC); - -CREATE INDEX idx_post_featured_community_score ON post USING btree (community_id, featured_community DESC, score DESC, published DESC, id DESC); - -CREATE INDEX idx_post_featured_local_active ON post USING btree (featured_local DESC, hot_rank_active DESC, published DESC, id DESC); - -CREATE INDEX idx_post_featured_local_controversy ON post USING btree (featured_local DESC, controversy_rank DESC, id DESC); - -CREATE INDEX idx_post_featured_local_hot ON post USING btree (featured_local DESC, hot_rank DESC, published DESC, id DESC); - -CREATE INDEX idx_post_featured_local_most_comments ON post USING btree (featured_local DESC, comments DESC, published DESC, id DESC); - -CREATE INDEX idx_post_featured_local_newest_comment_time ON post USING btree (featured_local DESC, newest_comment_time DESC, id DESC); - -CREATE INDEX idx_post_featured_local_newest_comment_time_necro ON post USING btree (featured_local DESC, newest_comment_time_necro DESC, id DESC); - -CREATE INDEX idx_post_featured_local_published ON post USING btree (featured_local DESC, published DESC, id DESC); - -CREATE INDEX idx_post_featured_local_published_asc ON post USING btree (featured_local DESC, reverse_timestamp_sort (published) DESC, id DESC); - -CREATE INDEX idx_post_featured_local_scaled ON post USING btree (featured_local DESC, scaled_rank DESC, published DESC, id DESC); - -CREATE INDEX idx_post_featured_local_score ON post USING btree (featured_local DESC, score DESC, published DESC, id DESC); - -CREATE INDEX idx_post_language ON post USING btree (language_id); - -CREATE INDEX idx_post_nonzero_hotrank ON post USING btree (published DESC) -WHERE ((hot_rank <> (0)::double precision) OR (hot_rank_active <> (0)::double precision)); - -CREATE INDEX idx_post_published ON post USING btree (published); - -CREATE INDEX idx_post_published_asc ON post USING btree (reverse_timestamp_sort (published) DESC); - -CREATE INDEX idx_post_scheduled_publish_time ON post USING btree (scheduled_publish_time); - -CREATE INDEX idx_post_trigram ON post USING gin (name gin_trgm_ops, body gin_trgm_ops, alt_text gin_trgm_ops); - -CREATE INDEX idx_post_url ON post USING btree (url); - -CREATE INDEX idx_post_url_content_type ON post USING gin (url_content_type gin_trgm_ops); - -ALTER TABLE ONLY post - ADD CONSTRAINT post_community_id_fkey FOREIGN KEY (community_id) REFERENCES community (id) ON UPDATE CASCADE ON DELETE CASCADE; - -ALTER TABLE ONLY post - ADD CONSTRAINT post_creator_id_fkey FOREIGN KEY (creator_id) REFERENCES person (id) ON UPDATE CASCADE ON DELETE CASCADE; - -ALTER TABLE ONLY post - ADD CONSTRAINT post_language_id_fkey FOREIGN KEY (language_id) REFERENCES LANGUAGE (id); - -ALTER TABLE ONLY post - ADD CONSTRAINT post_instance_id_fkey FOREIGN KEY (instance_id) REFERENCES instance (id) ON UPDATE CASCADE ON DELETE CASCADE; - -ALTER TABLE post - ALTER COLUMN name SET NOT NULL; - -ALTER TABLE post - ALTER COLUMN creator_id SET NOT NULL; - -ALTER TABLE post - ALTER COLUMN community_id SET NOT NULL; - -ALTER TABLE post - ALTER COLUMN ap_id SET NOT NULL; - diff --git a/migrations/2025-08-01-000046_remove_post_instance_id/up.sql b/migrations/2025-08-01-000046_remove_post_instance_id/up.sql deleted file mode 100644 index 5ced97212..000000000 --- a/migrations/2025-08-01-000046_remove_post_instance_id/up.sql +++ /dev/null @@ -1,3 +0,0 @@ -ALTER TABLE post - DROP COLUMN instance_id; - diff --git a/migrations/2025-08-01-000048_cursor_pagination_indexes/up.sql b/migrations/2025-08-01-000048_cursor_pagination_indexes/up.sql index caead6ab5..c4ee75549 100644 --- a/migrations/2025-08-01-000048_cursor_pagination_indexes/up.sql +++ b/migrations/2025-08-01-000048_cursor_pagination_indexes/up.sql @@ -2,13 +2,13 @@ CREATE INDEX idx_tagline_published_id ON tagline (published DESC, id DESC); -- Some for the vote views -CREATE INDEX idx_comment_actions_like_score ON comment_actions (comment_id, like_score, person_id) +CREATE INDEX idx_comment_actions_like_score ON comment_actions (comment_id, vote_is_upvote, person_id) WHERE - like_score IS NOT NULL; + vote_is_upvote IS NOT NULL; -CREATE INDEX idx_post_actions_like_score ON post_actions (post_id, like_score, person_id) +CREATE INDEX idx_post_actions_like_score ON post_actions (post_id, vote_is_upvote, person_id) WHERE - like_score IS NOT NULL; + vote_is_upvote IS NOT NULL; -- Fixing the community sorts for an id tie-breaker DROP INDEX idx_community_lower_name; diff --git a/migrations/2025-08-01-000049_add_liked_combined/up.sql b/migrations/2025-08-01-000049_add_liked_combined/up.sql index 47e7ff446..a1b311ef6 100644 --- a/migrations/2025-08-01-000049_add_liked_combined/up.sql +++ b/migrations/2025-08-01-000049_add_liked_combined/up.sql @@ -2,13 +2,22 @@ -- person_liked: (comment, post) -- This one is special, because you use the liked date, not the ordinary published -- Updating the history +CREATE SEQUENCE person_liked_combined_id_seq + AS integer START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + CREATE TABLE person_liked_combined AS SELECT pa.liked, - pa.like_score, + -- `ADD COLUMN id serial` is not used for this because it would require either putting the column at the end (might increase the amount of padding bytes) or using an `INSERT` statement (not parallelizable). + nextval('person_liked_combined_id_seq'::regclass)::int AS id, pa.person_id, pa.post_id, - NULL::int AS comment_id + NULL::int AS comment_id, + pa.vote_is_upvote FROM post_actions pa INNER JOIN person p ON pa.person_id = p.id @@ -18,10 +27,11 @@ WHERE UNION ALL SELECT ca.liked, - ca.like_score, + nextval('person_liked_combined_id_seq'::regclass)::int, ca.person_id, NULL::int, - ca.comment_id + ca.comment_id, + ca.vote_is_upvote FROM comment_actions ca INNER JOIN person p ON ca.person_id = p.id @@ -30,16 +40,19 @@ WHERE AND p.local = TRUE; ALTER TABLE person_liked_combined - ADD COLUMN id int PRIMARY KEY GENERATED ALWAYS AS IDENTITY, + ALTER COLUMN id SET DEFAULT nextval('person_liked_combined_id_seq'::regclass), ALTER COLUMN liked SET NOT NULL, - ALTER COLUMN like_score SET NOT NULL, + ALTER COLUMN vote_is_upvote SET NOT NULL, ALTER COLUMN person_id SET NOT NULL, ADD CONSTRAINT person_liked_combined_person_id_fkey FOREIGN KEY (person_id) REFERENCES person ON UPDATE CASCADE ON DELETE CASCADE, ADD CONSTRAINT person_liked_combined_post_id_fkey FOREIGN KEY (post_id) REFERENCES post ON UPDATE CASCADE ON DELETE CASCADE, ADD CONSTRAINT person_liked_combined_comment_id_fkey FOREIGN KEY (comment_id) REFERENCES COMMENT ON UPDATE CASCADE ON DELETE CASCADE, ADD UNIQUE (person_id, post_id), ADD UNIQUE (person_id, comment_id), + ADD PRIMARY KEY (id), ADD CONSTRAINT person_liked_combined_check CHECK (num_nonnulls (post_id, comment_id) = 1); +ALTER SEQUENCE person_liked_combined_id_seq OWNED BY person_liked_combined.id; + CREATE INDEX idx_person_liked_combined ON person_liked_combined (person_id); diff --git a/migrations/2025-08-01-000055_rename_timestamp_add_at/down.sql b/migrations/2025-08-01-000055_rename_timestamp_add_at/down.sql index d185869be..7a76008e4 100644 --- a/migrations/2025-08-01-000055_rename_timestamp_add_at/down.sql +++ b/migrations/2025-08-01-000055_rename_timestamp_add_at/down.sql @@ -18,7 +18,7 @@ ALTER TABLE comment RENAME COLUMN published_at TO published; ALTER TABLE comment RENAME COLUMN updated_at TO updated; -ALTER TABLE comment_actions RENAME COLUMN liked_at TO liked; +ALTER TABLE comment_actions RENAME COLUMN voted_at TO liked; ALTER TABLE comment_actions RENAME COLUMN saved_at TO saved; @@ -148,7 +148,7 @@ ALTER TABLE person_comment_mention RENAME COLUMN published_at TO published; ALTER TABLE person_content_combined RENAME COLUMN published_at TO published; -ALTER TABLE person_liked_combined RENAME COLUMN liked_at TO liked; +ALTER TABLE person_liked_combined RENAME COLUMN voted_at TO liked; ALTER TABLE person_post_mention RENAME COLUMN published_at TO published; @@ -170,7 +170,7 @@ ALTER TABLE post_actions RENAME COLUMN read_comments_at TO read_comments; ALTER TABLE post_actions RENAME COLUMN saved_at TO saved; -ALTER TABLE post_actions RENAME COLUMN liked_at TO liked; +ALTER TABLE post_actions RENAME COLUMN voted_at TO liked; ALTER TABLE post_actions RENAME COLUMN hidden_at TO hidden; diff --git a/migrations/2025-08-01-000055_rename_timestamp_add_at/up.sql b/migrations/2025-08-01-000055_rename_timestamp_add_at/up.sql index 2361967fc..edda1a016 100644 --- a/migrations/2025-08-01-000055_rename_timestamp_add_at/up.sql +++ b/migrations/2025-08-01-000055_rename_timestamp_add_at/up.sql @@ -18,7 +18,7 @@ ALTER TABLE comment RENAME COLUMN published TO published_at; ALTER TABLE comment RENAME COLUMN updated TO updated_at; -ALTER TABLE comment_actions RENAME COLUMN liked TO liked_at; +ALTER TABLE comment_actions RENAME COLUMN liked TO voted_at; ALTER TABLE comment_actions RENAME COLUMN saved TO saved_at; @@ -148,7 +148,7 @@ ALTER TABLE person_comment_mention RENAME COLUMN published TO published_at; ALTER TABLE person_content_combined RENAME COLUMN published TO published_at; -ALTER TABLE person_liked_combined RENAME COLUMN liked TO liked_at; +ALTER TABLE person_liked_combined RENAME COLUMN liked TO voted_at; ALTER TABLE person_post_mention RENAME COLUMN published TO published_at; @@ -170,7 +170,7 @@ ALTER TABLE post_actions RENAME COLUMN read_comments TO read_comments_at; ALTER TABLE post_actions RENAME COLUMN saved TO saved_at; -ALTER TABLE post_actions RENAME COLUMN liked TO liked_at; +ALTER TABLE post_actions RENAME COLUMN liked TO voted_at; ALTER TABLE post_actions RENAME COLUMN hidden TO hidden_at; diff --git a/migrations/2025-08-01-000059_person_votes/up.sql b/migrations/2025-08-01-000059_person_votes/up.sql index 0b85c43d2..7abe7ddd6 100644 --- a/migrations/2025-08-01-000059_person_votes/up.sql +++ b/migrations/2025-08-01-000059_person_votes/up.sql @@ -20,13 +20,13 @@ SELECT votes.person_id, votes.creator_id, now(), - count(*) FILTER (WHERE votes.like_score = 1) AS upvotes, - count(*) FILTER (WHERE votes.like_score != 1) AS downvotes + count(*) FILTER (WHERE votes.vote_is_upvote) AS upvotes, + count(*) FILTER (WHERE NOT votes.vote_is_upvote) AS downvotes FROM ( SELECT pa.person_id, p.creator_id, - like_score + vote_is_upvote FROM post_actions pa INNER JOIN post p ON pa.post_id = p.id @@ -35,7 +35,7 @@ FROM ( SELECT ca.person_id, c.creator_id, - like_score + vote_is_upvote FROM comment_actions ca INNER JOIN comment c ON ca.comment_id = c.id diff --git a/migrations/2025-08-06-170325_add_indexes_for_aggregates_activity_new/down.sql b/migrations/2025-08-06-170325_add_indexes_for_aggregates_activity_new/down.sql index b9f519fde..35250872a 100644 --- a/migrations/2025-08-06-170325_add_indexes_for_aggregates_activity_new/down.sql +++ b/migrations/2025-08-06-170325_add_indexes_for_aggregates_activity_new/down.sql @@ -1,2 +1,2 @@ -DROP INDEX idx_post_actions_liked_at, idx_comment_actions_liked_at; +DROP INDEX idx_post_actions_voted_at, idx_comment_actions_voted_at; diff --git a/migrations/2025-08-06-170325_add_indexes_for_aggregates_activity_new/up.sql b/migrations/2025-08-06-170325_add_indexes_for_aggregates_activity_new/up.sql index ff39a7ad1..9d49be2c5 100644 --- a/migrations/2025-08-06-170325_add_indexes_for_aggregates_activity_new/up.sql +++ b/migrations/2025-08-06-170325_add_indexes_for_aggregates_activity_new/up.sql @@ -1,8 +1,8 @@ -CREATE INDEX idx_post_actions_liked_at ON post_actions (liked_at) +CREATE INDEX idx_post_actions_voted_at ON post_actions (voted_at) WHERE - liked_at IS NOT NULL; + voted_at IS NOT NULL; -CREATE INDEX idx_comment_actions_liked_at ON comment_actions (liked_at) +CREATE INDEX idx_comment_actions_voted_at ON comment_actions (voted_at) WHERE - liked_at IS NOT NULL; + voted_at IS NOT NULL;