From e32ab8aef278ebcef7ecbc9ae67f417b6862cff9 Mon Sep 17 00:00:00 2001 From: Phantasm Date: Tue, 14 Oct 2025 22:59:15 +0200 Subject: [PATCH 1/2] DB prune: Check if user follows hashtag with no objects before deletion --- changelog.d/prune-hashtag-follow-3376.fix | 1 + lib/mix/tasks/pleroma/database.ex | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 changelog.d/prune-hashtag-follow-3376.fix diff --git a/changelog.d/prune-hashtag-follow-3376.fix b/changelog.d/prune-hashtag-follow-3376.fix new file mode 100644 index 000000000..cdb4e9a79 --- /dev/null +++ b/changelog.d/prune-hashtag-follow-3376.fix @@ -0,0 +1 @@ +DB prune: Check if user follows hashtag with no objects before deletion diff --git a/lib/mix/tasks/pleroma/database.ex b/lib/mix/tasks/pleroma/database.ex index e52b5e0a7..396536827 100644 --- a/lib/mix/tasks/pleroma/database.ex +++ b/lib/mix/tasks/pleroma/database.ex @@ -226,7 +226,12 @@ defmodule Mix.Tasks.Pleroma.Database do DELETE FROM hashtags AS ht WHERE NOT EXISTS ( SELECT 1 FROM hashtags_objects hto - WHERE ht.id = hto.hashtag_id) + WHERE ht.id = hto.hashtag_id + ) + AND NOT EXISTS ( + SELECT 1 FROM user_follows_hashtag ufh + WHERE ht.id = ufh.hashtag_id + ) """ |> Repo.query() From ef7be0a1e513841beba15ccce6b6211b7369332a Mon Sep 17 00:00:00 2001 From: Phantasm Date: Wed, 15 Oct 2025 23:14:52 +0200 Subject: [PATCH 2/2] DB prune: Add test for hashtags --- test/mix/tasks/pleroma/database_test.exs | 34 ++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/test/mix/tasks/pleroma/database_test.exs b/test/mix/tasks/pleroma/database_test.exs index 19df17b60..5b567325c 100644 --- a/test/mix/tasks/pleroma/database_test.exs +++ b/test/mix/tasks/pleroma/database_test.exs @@ -8,6 +8,7 @@ defmodule Mix.Tasks.Pleroma.DatabaseTest do alias Pleroma.Activity alias Pleroma.Bookmark + alias Pleroma.Hashtag alias Pleroma.Object alias Pleroma.Repo alias Pleroma.User @@ -550,6 +551,39 @@ defmodule Mix.Tasks.Pleroma.DatabaseTest do assert length(activities) == 3 end + + test "it prunes hashtags with no objects associated", %{old_insert_date: old_insert_date} do + user = insert(:user) + + {:ok, hashtag_post_activity} = + CommonAPI.post(user, %{status: "morning #cofe", local: true}) + + hashtag_post_object = Object.normalize(hashtag_post_activity) + + {:ok, hashtag_post2_activity} = + CommonAPI.post(user, %{status: "morning #cawfee", local: true}) + + hashtag_post2_object = Object.normalize(hashtag_post2_activity) + + hashtag_post_object + |> Ecto.Changeset.change(%{updated_at: old_insert_date}) + |> Repo.update!() + + hashtag_post2_object + |> Ecto.Changeset.change(%{updated_at: old_insert_date}) + |> Repo.update!() + + # Test whether hashtags with follow relationships are kept + User.follow_hashtag(user, Hashtag.get_by_name("cofe")) + + assert length(Repo.all(Hashtag)) == 2 + assert length(Repo.all(Object)) == 2 + + Mix.Tasks.Pleroma.Database.run(["prune_objects"]) + assert length(Repo.all(Hashtag)) == 1 + assert length(Repo.all(Object)) == 0 + assert Repo.one(Hashtag) |> Map.fetch!(:name) == "cofe" + end end describe "running update_users_following_followers_counts" do