From 8429d074548c34fc171ccaa802bfbb2bfb6d3d06 Mon Sep 17 00:00:00 2001
From: Matt Jankowski <matt@jankowski.online>
Date: Mon, 26 Feb 2024 08:45:39 -0500
Subject: [PATCH] Add `RankedTrend` concern for trends classes (#29388)

---
 app/models/concerns/ranked_trend.rb | 29 +++++++++++++++++++++++++++++
 app/models/preview_card_trend.rb    |  2 ++
 app/models/status_trend.rb          |  2 ++
 app/models/trends/links.rb          |  4 ++--
 app/models/trends/statuses.rb       |  4 ++--
 5 files changed, 37 insertions(+), 4 deletions(-)
 create mode 100644 app/models/concerns/ranked_trend.rb

diff --git a/app/models/concerns/ranked_trend.rb b/app/models/concerns/ranked_trend.rb
new file mode 100644
index 000000000..add36afb0
--- /dev/null
+++ b/app/models/concerns/ranked_trend.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+module RankedTrend
+  extend ActiveSupport::Concern
+
+  included do
+    scope :by_rank, -> { order(rank: :desc) }
+    scope :ranked_below, ->(value) { where(rank: ..value) }
+  end
+
+  class_methods do
+    def recalculate_ordered_rank
+      connection
+        .exec_update(<<~SQL.squish)
+          UPDATE #{table_name}
+          SET rank = inner_ordered.calculated_rank
+          FROM (
+            SELECT id, row_number() OVER w AS calculated_rank
+            FROM #{table_name}
+            WINDOW w AS (
+              PARTITION BY language
+              ORDER BY score DESC
+            )
+          ) inner_ordered
+          WHERE #{table_name}.id = inner_ordered.id
+        SQL
+    end
+  end
+end
diff --git a/app/models/preview_card_trend.rb b/app/models/preview_card_trend.rb
index 018400dfa..da2ea2f8c 100644
--- a/app/models/preview_card_trend.rb
+++ b/app/models/preview_card_trend.rb
@@ -12,6 +12,8 @@
 #  language        :string
 #
 class PreviewCardTrend < ApplicationRecord
+  include RankedTrend
+
   belongs_to :preview_card
   scope :allowed, -> { where(allowed: true) }
 end
diff --git a/app/models/status_trend.rb b/app/models/status_trend.rb
index b0f1b6942..807efec0f 100644
--- a/app/models/status_trend.rb
+++ b/app/models/status_trend.rb
@@ -14,6 +14,8 @@
 #
 
 class StatusTrend < ApplicationRecord
+  include RankedTrend
+
   belongs_to :status
   belongs_to :account
 
diff --git a/app/models/trends/links.rb b/app/models/trends/links.rb
index b4eae9f70..76e50aa7a 100644
--- a/app/models/trends/links.rb
+++ b/app/models/trends/links.rb
@@ -81,12 +81,12 @@ class Trends::Links < Trends::Base
 
     # Now that all trends have up-to-date scores, and all the ones below the threshold have
     # been removed, we can recalculate their positions
-    PreviewCardTrend.connection.exec_update('UPDATE preview_card_trends SET rank = t0.calculated_rank FROM (SELECT id, row_number() OVER w AS calculated_rank FROM preview_card_trends WINDOW w AS (PARTITION BY language ORDER BY score DESC)) t0 WHERE preview_card_trends.id = t0.id')
+    PreviewCardTrend.recalculate_ordered_rank
   end
 
   def request_review
     PreviewCardTrend.pluck('distinct language').flat_map do |language|
-      score_at_threshold  = PreviewCardTrend.where(language: language, allowed: true).order(rank: :desc).where('rank <= ?', options[:review_threshold]).first&.score || 0
+      score_at_threshold  = PreviewCardTrend.where(language: language, allowed: true).by_rank.ranked_below(options[:review_threshold]).first&.score || 0
       preview_card_trends = PreviewCardTrend.where(language: language, allowed: false).joins(:preview_card)
 
       preview_card_trends.filter_map do |trend|
diff --git a/app/models/trends/statuses.rb b/app/models/trends/statuses.rb
index c47fb8427..9be6eb13a 100644
--- a/app/models/trends/statuses.rb
+++ b/app/models/trends/statuses.rb
@@ -74,12 +74,12 @@ class Trends::Statuses < Trends::Base
 
     # Now that all trends have up-to-date scores, and all the ones below the threshold have
     # been removed, we can recalculate their positions
-    StatusTrend.connection.exec_update('UPDATE status_trends SET rank = t0.calculated_rank FROM (SELECT id, row_number() OVER w AS calculated_rank FROM status_trends WINDOW w AS (PARTITION BY language ORDER BY score DESC)) t0 WHERE status_trends.id = t0.id')
+    StatusTrend.recalculate_ordered_rank
   end
 
   def request_review
     StatusTrend.pluck('distinct language').flat_map do |language|
-      score_at_threshold = StatusTrend.where(language: language, allowed: true).order(rank: :desc).where('rank <= ?', options[:review_threshold]).first&.score || 0
+      score_at_threshold = StatusTrend.where(language: language, allowed: true).by_rank.ranked_below(options[:review_threshold]).first&.score || 0
       status_trends      = StatusTrend.where(language: language, allowed: false).joins(:status).includes(status: :account)
 
       status_trends.filter_map do |trend|