about summary refs log tree commit diff
path: root/db
diff options
context:
space:
mode:
Diffstat (limited to 'db')
-rw-r--r--db/migrate/20210306164523_account_ids_to_timestamp_ids.rb17
-rw-r--r--db/migrate/20210322164601_create_account_summaries.rb9
-rw-r--r--db/migrate/20210323114347_create_follow_recommendations.rb5
-rw-r--r--db/migrate/20210324171613_create_follow_recommendation_suppressions.rb9
-rw-r--r--db/migrate/20210416200740_create_canonical_email_blocks.rb10
-rw-r--r--db/migrate/20210421121431_add_case_insensitive_btree_index_to_tags.rb13
-rw-r--r--db/migrate/20210425135952_add_index_on_media_attachments_account_id_status_id.rb13
-rw-r--r--db/migrate/20210505174616_update_follow_recommendations_to_version_2.rb18
-rw-r--r--db/post_migrate/20210502233513_drop_account_tag_stats.rb13
-rw-r--r--db/post_migrate/20210507001928_remove_hub_url_from_accounts.rb12
-rw-r--r--db/schema.rb97
-rw-r--r--db/views/account_summaries_v01.sql22
-rw-r--r--db/views/follow_recommendations_v01.sql38
-rw-r--r--db/views/follow_recommendations_v02.sql34
14 files changed, 287 insertions, 23 deletions
diff --git a/db/migrate/20210306164523_account_ids_to_timestamp_ids.rb b/db/migrate/20210306164523_account_ids_to_timestamp_ids.rb
new file mode 100644
index 000000000..39cd4cdea
--- /dev/null
+++ b/db/migrate/20210306164523_account_ids_to_timestamp_ids.rb
@@ -0,0 +1,17 @@
+class AccountIdsToTimestampIds < ActiveRecord::Migration[5.1]
+  def up
+    # Set up the accounts.id column to use our timestamp-based IDs.
+    safety_assured do
+      execute("ALTER TABLE accounts ALTER COLUMN id SET DEFAULT timestamp_id('accounts')")
+    end
+
+    # Make sure we have a sequence to use.
+    Mastodon::Snowflake.ensure_id_sequences_exist
+  end
+
+  def down
+    execute("LOCK accounts")
+    execute("SELECT setval('accounts_id_seq', (SELECT MAX(id) FROM accounts))")
+    execute("ALTER TABLE accounts ALTER COLUMN id SET DEFAULT nextval('accounts_id_seq')")
+  end
+end
diff --git a/db/migrate/20210322164601_create_account_summaries.rb b/db/migrate/20210322164601_create_account_summaries.rb
new file mode 100644
index 000000000..bc9011113
--- /dev/null
+++ b/db/migrate/20210322164601_create_account_summaries.rb
@@ -0,0 +1,9 @@
+class CreateAccountSummaries < ActiveRecord::Migration[5.2]
+  def change
+    create_view :account_summaries, materialized: { no_data: true }
+
+    # To be able to refresh the view concurrently,
+    # at least one unique index is required
+    safety_assured { add_index :account_summaries, :account_id, unique: true }
+  end
+end
diff --git a/db/migrate/20210323114347_create_follow_recommendations.rb b/db/migrate/20210323114347_create_follow_recommendations.rb
new file mode 100644
index 000000000..77e729032
--- /dev/null
+++ b/db/migrate/20210323114347_create_follow_recommendations.rb
@@ -0,0 +1,5 @@
+class CreateFollowRecommendations < ActiveRecord::Migration[5.2]
+  def change
+    create_view :follow_recommendations
+  end
+end
diff --git a/db/migrate/20210324171613_create_follow_recommendation_suppressions.rb b/db/migrate/20210324171613_create_follow_recommendation_suppressions.rb
new file mode 100644
index 000000000..c17a0be63
--- /dev/null
+++ b/db/migrate/20210324171613_create_follow_recommendation_suppressions.rb
@@ -0,0 +1,9 @@
+class CreateFollowRecommendationSuppressions < ActiveRecord::Migration[6.1]
+  def change
+    create_table :follow_recommendation_suppressions do |t|
+      t.references :account, null: false, foreign_key: { on_delete: :cascade }, index: { unique: true }
+
+      t.timestamps
+    end
+  end
+end
diff --git a/db/migrate/20210416200740_create_canonical_email_blocks.rb b/db/migrate/20210416200740_create_canonical_email_blocks.rb
new file mode 100644
index 000000000..a1f1660bf
--- /dev/null
+++ b/db/migrate/20210416200740_create_canonical_email_blocks.rb
@@ -0,0 +1,10 @@
+class CreateCanonicalEmailBlocks < ActiveRecord::Migration[6.1]
+  def change
+    create_table :canonical_email_blocks do |t|
+      t.string :canonical_email_hash, null: false, default: '', index: { unique: true }
+      t.belongs_to :reference_account, null: false, foreign_key: { on_cascade: :delete, to_table: 'accounts' }
+
+      t.timestamps
+    end
+  end
+end
diff --git a/db/migrate/20210421121431_add_case_insensitive_btree_index_to_tags.rb b/db/migrate/20210421121431_add_case_insensitive_btree_index_to_tags.rb
new file mode 100644
index 000000000..ed359e8cd
--- /dev/null
+++ b/db/migrate/20210421121431_add_case_insensitive_btree_index_to_tags.rb
@@ -0,0 +1,13 @@
+class AddCaseInsensitiveBtreeIndexToTags < ActiveRecord::Migration[5.2]
+  disable_ddl_transaction!
+
+  def up
+    safety_assured { execute 'CREATE UNIQUE INDEX CONCURRENTLY index_tags_on_name_lower_btree ON tags (lower(name) text_pattern_ops)' }
+    remove_index :tags, name: 'index_tags_on_name_lower'
+  end
+
+  def down
+    safety_assured { execute 'CREATE UNIQUE INDEX CONCURRENTLY index_tags_on_name_lower ON tags (lower(name))' }
+    remove_index :tags, name: 'index_tags_on_name_lower_btree'
+  end
+end
diff --git a/db/migrate/20210425135952_add_index_on_media_attachments_account_id_status_id.rb b/db/migrate/20210425135952_add_index_on_media_attachments_account_id_status_id.rb
new file mode 100644
index 000000000..5ef2d3c39
--- /dev/null
+++ b/db/migrate/20210425135952_add_index_on_media_attachments_account_id_status_id.rb
@@ -0,0 +1,13 @@
+class AddIndexOnMediaAttachmentsAccountIdStatusId < ActiveRecord::Migration[5.2]
+  disable_ddl_transaction!
+
+  def up
+    add_index :media_attachments, [:account_id, :status_id], order: { status_id: :desc }, algorithm: :concurrently
+    remove_index :media_attachments, :account_id, algorithm: :concurrently
+  end
+
+  def down
+    add_index :media_attachments, :account_id, algorithm: :concurrently
+    remove_index :media_attachments, [:account_id, :status_id], order: { status_id: :desc }, algorithm: :concurrently
+  end
+end
diff --git a/db/migrate/20210505174616_update_follow_recommendations_to_version_2.rb b/db/migrate/20210505174616_update_follow_recommendations_to_version_2.rb
new file mode 100644
index 000000000..56c0b4cb0
--- /dev/null
+++ b/db/migrate/20210505174616_update_follow_recommendations_to_version_2.rb
@@ -0,0 +1,18 @@
+class UpdateFollowRecommendationsToVersion2 < ActiveRecord::Migration[6.1]
+  # We're switching from a normal to a materialized view so we need
+  # custom `up` and `down` paths.
+
+  def up
+    drop_view :follow_recommendations
+    create_view :follow_recommendations, version: 2, materialized: { no_data: true }
+
+    # To be able to refresh the view concurrently,
+    # at least one unique index is required
+    safety_assured { add_index :follow_recommendations, :account_id, unique: true }
+  end
+
+  def down
+    drop_view :follow_recommendations, materialized: true
+    create_view :follow_recommendations, version: 1
+  end
+end
diff --git a/db/post_migrate/20210502233513_drop_account_tag_stats.rb b/db/post_migrate/20210502233513_drop_account_tag_stats.rb
new file mode 100644
index 000000000..80adadcab
--- /dev/null
+++ b/db/post_migrate/20210502233513_drop_account_tag_stats.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+class DropAccountTagStats < ActiveRecord::Migration[5.2]
+  disable_ddl_transaction!
+
+  def up
+    drop_table :account_tag_stats
+  end
+
+  def down
+    raise ActiveRecord::IrreversibleMigration
+  end
+end
diff --git a/db/post_migrate/20210507001928_remove_hub_url_from_accounts.rb b/db/post_migrate/20210507001928_remove_hub_url_from_accounts.rb
new file mode 100644
index 000000000..83a1f5fcf
--- /dev/null
+++ b/db/post_migrate/20210507001928_remove_hub_url_from_accounts.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+class RemoveHubURLFromAccounts < ActiveRecord::Migration[5.2]
+  def change
+    safety_assured do
+      remove_column :accounts, :secret, :string, null: false, default: ''
+      remove_column :accounts, :remote_url, :string, null: false, default: ''
+      remove_column :accounts, :salmon_url, :string, null: false, default: ''
+      remove_column :accounts, :hub_url, :string, null: false, default: ''
+    end
+  end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 4c67353fb..5cf5f665d 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -2,15 +2,15 @@
 # of editing this file, please use the migrations feature of Active Record to
 # incrementally modify your database, and then regenerate this schema definition.
 #
-# Note that this schema.rb definition is the authoritative source for your
-# database schema. If you need to create the application database on another
-# system, you should be using db:schema:load, not running all the migrations
-# from scratch. The latter is a flawed and unsustainable approach (the more migrations
-# you'll amass, the slower it'll run and the greater likelihood for issues).
+# This file is the source Rails uses to define your schema when running `bin/rails
+# db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to
+# be faster and is potentially less error prone than running all of your
+# migrations from scratch. Old migrations may fail to apply correctly if those
+# migrations use external dependencies or application code.
 #
 # It's strongly recommended that you check this file into your version control system.
 
-ActiveRecord::Schema.define(version: 2021_03_08_133107) do
+ActiveRecord::Schema.define(version: 2021_05_07_001928) do
 
   # These are extensions that must be enabled in order to support this database
   enable_extension "plpgsql"
@@ -115,15 +115,6 @@ ActiveRecord::Schema.define(version: 2021_03_08_133107) do
     t.index ["account_id"], name: "index_account_stats_on_account_id", unique: true
   end
 
-  create_table "account_tag_stats", force: :cascade do |t|
-    t.bigint "tag_id", null: false
-    t.bigint "accounts_count", default: 0, null: false
-    t.boolean "hidden", default: false, null: false
-    t.datetime "created_at", null: false
-    t.datetime "updated_at", null: false
-    t.index ["tag_id"], name: "index_account_tag_stats_on_tag_id", unique: true
-  end
-
   create_table "account_warning_presets", force: :cascade do |t|
     t.text "text", default: "", null: false
     t.datetime "created_at", null: false
@@ -142,15 +133,11 @@ ActiveRecord::Schema.define(version: 2021_03_08_133107) do
     t.index ["target_account_id"], name: "index_account_warnings_on_target_account_id"
   end
 
-  create_table "accounts", force: :cascade do |t|
+  create_table "accounts", id: :bigint, default: -> { "timestamp_id('accounts'::text)" }, force: :cascade do |t|
     t.string "username", default: "", null: false
     t.string "domain"
-    t.string "secret", default: "", null: false
     t.text "private_key"
     t.text "public_key", default: "", null: false
-    t.string "remote_url", default: "", null: false
-    t.string "salmon_url", default: "", null: false
-    t.string "hub_url", default: "", null: false
     t.datetime "created_at", null: false
     t.datetime "updated_at", null: false
     t.text "note", default: "", null: false
@@ -280,6 +267,15 @@ ActiveRecord::Schema.define(version: 2021_03_08_133107) do
     t.index ["status_id"], name: "index_bookmarks_on_status_id"
   end
 
+  create_table "canonical_email_blocks", force: :cascade do |t|
+    t.string "canonical_email_hash", default: "", null: false
+    t.bigint "reference_account_id", null: false
+    t.datetime "created_at", precision: 6, null: false
+    t.datetime "updated_at", precision: 6, null: false
+    t.index ["canonical_email_hash"], name: "index_canonical_email_blocks_on_canonical_email_hash", unique: true
+    t.index ["reference_account_id"], name: "index_canonical_email_blocks_on_reference_account_id"
+  end
+
   create_table "conversation_mutes", force: :cascade do |t|
     t.bigint "conversation_id", null: false
     t.bigint "account_id", null: false
@@ -406,6 +402,13 @@ ActiveRecord::Schema.define(version: 2021_03_08_133107) do
     t.index ["tag_id"], name: "index_featured_tags_on_tag_id"
   end
 
+  create_table "follow_recommendation_suppressions", force: :cascade do |t|
+    t.bigint "account_id", null: false
+    t.datetime "created_at", precision: 6, null: false
+    t.datetime "updated_at", precision: 6, null: false
+    t.index ["account_id"], name: "index_follow_recommendation_suppressions_on_account_id", unique: true
+  end
+
   create_table "follow_requests", force: :cascade do |t|
     t.datetime "created_at", null: false
     t.datetime "updated_at", null: false
@@ -525,7 +528,7 @@ ActiveRecord::Schema.define(version: 2021_03_08_133107) do
     t.integer "thumbnail_file_size"
     t.datetime "thumbnail_updated_at"
     t.string "thumbnail_remote_url"
-    t.index ["account_id"], name: "index_media_attachments_on_account_id"
+    t.index ["account_id", "status_id"], name: "index_media_attachments_on_account_id_and_status_id", order: { status_id: :desc }
     t.index ["scheduled_status_id"], name: "index_media_attachments_on_scheduled_status_id"
     t.index ["shortcode"], name: "index_media_attachments_on_shortcode", unique: true
     t.index ["status_id"], name: "index_media_attachments_on_status_id"
@@ -848,7 +851,7 @@ ActiveRecord::Schema.define(version: 2021_03_08_133107) do
     t.datetime "last_status_at"
     t.float "max_score"
     t.datetime "max_score_at"
-    t.index "lower((name)::text)", name: "index_tags_on_name_lower", unique: true
+    t.index "lower((name)::text) text_pattern_ops", name: "index_tags_on_name_lower_btree", unique: true
   end
 
   create_table "tombstones", force: :cascade do |t|
@@ -971,7 +974,6 @@ ActiveRecord::Schema.define(version: 2021_03_08_133107) do
   add_foreign_key "account_pins", "accounts", column: "target_account_id", on_delete: :cascade
   add_foreign_key "account_pins", "accounts", on_delete: :cascade
   add_foreign_key "account_stats", "accounts", on_delete: :cascade
-  add_foreign_key "account_tag_stats", "tags", on_delete: :cascade
   add_foreign_key "account_warnings", "accounts", column: "target_account_id", on_delete: :cascade
   add_foreign_key "account_warnings", "accounts", on_delete: :nullify
   add_foreign_key "accounts", "accounts", column: "moved_to_account_id", on_delete: :nullify
@@ -986,6 +988,7 @@ ActiveRecord::Schema.define(version: 2021_03_08_133107) do
   add_foreign_key "blocks", "accounts", name: "fk_4269e03e65", on_delete: :cascade
   add_foreign_key "bookmarks", "accounts", on_delete: :cascade
   add_foreign_key "bookmarks", "statuses", on_delete: :cascade
+  add_foreign_key "canonical_email_blocks", "accounts", column: "reference_account_id"
   add_foreign_key "conversation_mutes", "accounts", name: "fk_225b4212bb", on_delete: :cascade
   add_foreign_key "conversation_mutes", "conversations", on_delete: :cascade
   add_foreign_key "custom_filters", "accounts", on_delete: :cascade
@@ -998,6 +1001,7 @@ ActiveRecord::Schema.define(version: 2021_03_08_133107) do
   add_foreign_key "favourites", "statuses", name: "fk_b0e856845e", on_delete: :cascade
   add_foreign_key "featured_tags", "accounts", on_delete: :cascade
   add_foreign_key "featured_tags", "tags", on_delete: :cascade
+  add_foreign_key "follow_recommendation_suppressions", "accounts", on_delete: :cascade
   add_foreign_key "follow_requests", "accounts", column: "target_account_id", name: "fk_9291ec025d", on_delete: :cascade
   add_foreign_key "follow_requests", "accounts", name: "fk_76d644b0e7", on_delete: :cascade
   add_foreign_key "follows", "accounts", column: "target_account_id", name: "fk_745ca29eac", on_delete: :cascade
@@ -1081,4 +1085,51 @@ ActiveRecord::Schema.define(version: 2021_03_08_133107) do
   SQL
   add_index "instances", ["domain"], name: "index_instances_on_domain", unique: true
 
+  create_view "account_summaries", materialized: true, sql_definition: <<-SQL
+      SELECT accounts.id AS account_id,
+      mode() WITHIN GROUP (ORDER BY t0.language) AS language,
+      mode() WITHIN GROUP (ORDER BY t0.sensitive) AS sensitive
+     FROM (accounts
+       CROSS JOIN LATERAL ( SELECT statuses.account_id,
+              statuses.language,
+              statuses.sensitive
+             FROM statuses
+            WHERE ((statuses.account_id = accounts.id) AND (statuses.deleted_at IS NULL))
+            ORDER BY statuses.id DESC
+           LIMIT 20) t0)
+    WHERE ((accounts.suspended_at IS NULL) AND (accounts.silenced_at IS NULL) AND (accounts.moved_to_account_id IS NULL) AND (accounts.discoverable = true) AND (accounts.locked = false))
+    GROUP BY accounts.id;
+  SQL
+  add_index "account_summaries", ["account_id"], name: "index_account_summaries_on_account_id", unique: true
+
+  create_view "follow_recommendations", materialized: true, sql_definition: <<-SQL
+      SELECT t0.account_id,
+      sum(t0.rank) AS rank,
+      array_agg(t0.reason) AS reason
+     FROM ( SELECT account_summaries.account_id,
+              ((count(follows.id))::numeric / (1.0 + (count(follows.id))::numeric)) AS rank,
+              'most_followed'::text AS reason
+             FROM (((follows
+               JOIN account_summaries ON ((account_summaries.account_id = follows.target_account_id)))
+               JOIN users ON ((users.account_id = follows.account_id)))
+               LEFT JOIN follow_recommendation_suppressions ON ((follow_recommendation_suppressions.account_id = follows.target_account_id)))
+            WHERE ((users.current_sign_in_at >= (now() - 'P30D'::interval)) AND (account_summaries.sensitive = false) AND (follow_recommendation_suppressions.id IS NULL))
+            GROUP BY account_summaries.account_id
+           HAVING (count(follows.id) >= 5)
+          UNION ALL
+           SELECT account_summaries.account_id,
+              (sum((status_stats.reblogs_count + status_stats.favourites_count)) / (1.0 + sum((status_stats.reblogs_count + status_stats.favourites_count)))) AS rank,
+              'most_interactions'::text AS reason
+             FROM (((status_stats
+               JOIN statuses ON ((statuses.id = status_stats.status_id)))
+               JOIN account_summaries ON ((account_summaries.account_id = statuses.account_id)))
+               LEFT JOIN follow_recommendation_suppressions ON ((follow_recommendation_suppressions.account_id = statuses.account_id)))
+            WHERE ((statuses.id >= (((date_part('epoch'::text, (now() - 'P30D'::interval)) * (1000)::double precision))::bigint << 16)) AND (account_summaries.sensitive = false) AND (follow_recommendation_suppressions.id IS NULL))
+            GROUP BY account_summaries.account_id
+           HAVING (sum((status_stats.reblogs_count + status_stats.favourites_count)) >= (5)::numeric)) t0
+    GROUP BY t0.account_id
+    ORDER BY (sum(t0.rank)) DESC;
+  SQL
+  add_index "follow_recommendations", ["account_id"], name: "index_follow_recommendations_on_account_id", unique: true
+
 end
diff --git a/db/views/account_summaries_v01.sql b/db/views/account_summaries_v01.sql
new file mode 100644
index 000000000..5a632b622
--- /dev/null
+++ b/db/views/account_summaries_v01.sql
@@ -0,0 +1,22 @@
+SELECT
+  accounts.id AS account_id,
+  mode() WITHIN GROUP (ORDER BY language ASC) AS language,
+  mode() WITHIN GROUP (ORDER BY sensitive ASC) AS sensitive
+FROM accounts
+CROSS JOIN LATERAL (
+  SELECT
+    statuses.account_id,
+    statuses.language,
+    statuses.sensitive
+  FROM statuses
+  WHERE statuses.account_id = accounts.id
+    AND statuses.deleted_at IS NULL
+  ORDER BY statuses.id DESC
+  LIMIT 20
+) t0
+WHERE accounts.suspended_at IS NULL
+  AND accounts.silenced_at IS NULL
+  AND accounts.moved_to_account_id IS NULL
+  AND accounts.discoverable = 't'
+  AND accounts.locked = 'f'
+GROUP BY accounts.id
diff --git a/db/views/follow_recommendations_v01.sql b/db/views/follow_recommendations_v01.sql
new file mode 100644
index 000000000..799abeaee
--- /dev/null
+++ b/db/views/follow_recommendations_v01.sql
@@ -0,0 +1,38 @@
+SELECT
+  account_id,
+  sum(rank) AS rank,
+  array_agg(reason) AS reason
+FROM (
+  SELECT
+    accounts.id AS account_id,
+    count(follows.id) / (1.0 + count(follows.id)) AS rank,
+    'most_followed' AS reason
+  FROM follows
+  INNER JOIN accounts ON accounts.id = follows.target_account_id
+  INNER JOIN users ON users.account_id = follows.account_id
+  WHERE users.current_sign_in_at >= (now() - interval '30 days')
+    AND accounts.suspended_at IS NULL
+    AND accounts.moved_to_account_id IS NULL
+    AND accounts.silenced_at IS NULL
+    AND accounts.locked = 'f'
+    AND accounts.discoverable = 't'
+  GROUP BY accounts.id
+  HAVING count(follows.id) >= 5
+  UNION ALL
+  SELECT accounts.id AS account_id,
+         sum(reblogs_count + favourites_count) / (1.0 + sum(reblogs_count + favourites_count)) AS rank,
+         'most_interactions' AS reason
+  FROM status_stats
+  INNER JOIN statuses ON statuses.id = status_stats.status_id
+  INNER JOIN accounts ON accounts.id = statuses.account_id
+  WHERE statuses.id >= ((date_part('epoch', now() - interval '30 days') * 1000)::bigint << 16)
+    AND accounts.suspended_at IS NULL
+    AND accounts.moved_to_account_id IS NULL
+    AND accounts.silenced_at IS NULL
+    AND accounts.locked = 'f'
+    AND accounts.discoverable = 't'
+  GROUP BY accounts.id
+  HAVING sum(reblogs_count + favourites_count) >= 5
+) t0
+GROUP BY account_id
+ORDER BY rank DESC
diff --git a/db/views/follow_recommendations_v02.sql b/db/views/follow_recommendations_v02.sql
new file mode 100644
index 000000000..673c5cc85
--- /dev/null
+++ b/db/views/follow_recommendations_v02.sql
@@ -0,0 +1,34 @@
+SELECT
+  account_id,
+  sum(rank) AS rank,
+  array_agg(reason) AS reason
+FROM (
+  SELECT
+    account_summaries.account_id AS account_id,
+    count(follows.id) / (1.0 + count(follows.id)) AS rank,
+    'most_followed' AS reason
+  FROM follows
+  INNER JOIN account_summaries ON account_summaries.account_id = follows.target_account_id
+  INNER JOIN users ON users.account_id = follows.account_id
+  LEFT OUTER JOIN follow_recommendation_suppressions ON follow_recommendation_suppressions.account_id = follows.target_account_id
+  WHERE users.current_sign_in_at >= (now() - interval '30 days')
+    AND account_summaries.sensitive = 'f'
+    AND follow_recommendation_suppressions.id IS NULL
+  GROUP BY account_summaries.account_id
+  HAVING count(follows.id) >= 5
+  UNION ALL
+  SELECT account_summaries.account_id AS account_id,
+         sum(reblogs_count + favourites_count) / (1.0 + sum(reblogs_count + favourites_count)) AS rank,
+         'most_interactions' AS reason
+  FROM status_stats
+  INNER JOIN statuses ON statuses.id = status_stats.status_id
+  INNER JOIN account_summaries ON account_summaries.account_id = statuses.account_id
+  LEFT OUTER JOIN follow_recommendation_suppressions ON follow_recommendation_suppressions.account_id = statuses.account_id
+  WHERE statuses.id >= ((date_part('epoch', now() - interval '30 days') * 1000)::bigint << 16)
+    AND account_summaries.sensitive = 'f'
+    AND follow_recommendation_suppressions.id IS NULL
+  GROUP BY account_summaries.account_id
+  HAVING sum(reblogs_count + favourites_count) >= 5
+) t0
+GROUP BY account_id
+ORDER BY rank DESC