From b4e8adca474d23aeb4f0f7540b347ef49a3a1303 Mon Sep 17 00:00:00 2001 From: Jose Farias Date: Fri, 15 Nov 2024 18:17:42 -0600 Subject: [PATCH] Pre-calculate comment counts and activity scores --- app/helpers/bubbles_helper.rb | 2 +- app/models/account.rb | 4 ++-- app/models/bubble.rb | 17 ++++------------- app/models/bubble/boostable.rb | 1 + app/models/comment.rb | 10 ++++++++++ app/models/concerns/messageable.rb | 6 ++++++ app/models/message.rb | 15 +++++++++++---- ...41115234456_add_comments_count_to_bubbles.rb | 5 +++++ ...41115234505_add_activity_score_to_bubbles.rb | 5 +++++ db/schema.rb | 4 +++- test/fixtures/bubbles.yml | 4 ++++ test/models/bubble_test.rb | 10 ++++++---- test/models/comment_test.rb | 14 ++++++++++++++ 13 files changed, 72 insertions(+), 25 deletions(-) create mode 100644 db/migrate/20241115234456_add_comments_count_to_bubbles.rb create mode 100644 db/migrate/20241115234505_add_activity_score_to_bubbles.rb diff --git a/app/helpers/bubbles_helper.rb b/app/helpers/bubbles_helper.rb index a88cadc30..d56bc590f 100644 --- a/app/helpers/bubbles_helper.rb +++ b/app/helpers/bubbles_helper.rb @@ -9,7 +9,7 @@ module BubblesHelper def bubble_size(bubble) rank = - case bubble.activity_count + case bubble.activity_score when 0..5 then "one" when 6..10 then "two" when 11..25 then "three" diff --git a/app/models/account.rb b/app/models/account.rb index 101c96210..c7062e340 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -1,11 +1,11 @@ class Account < ApplicationRecord include Joinable - has_many :users, dependent: :destroy - has_many :buckets, dependent: :destroy has_many :bubbles, through: :buckets + has_many :users, dependent: :destroy + has_many :workflows, dependent: :destroy has_many :stages, through: :workflows, class_name: "Workflow::Stage" diff --git a/app/models/bubble.rb b/app/models/bubble.rb index a52cb6bb1..2b197b89b 100644 --- a/app/models/bubble.rb +++ b/app/models/bubble.rb @@ -10,19 +10,10 @@ class Bubble < ApplicationRecord scope :reverse_chronologically, -> { order created_at: :desc, id: :desc } scope :chronologically, -> { order created_at: :asc, id: :asc } + scope :ordered_by_activity, -> { order activity_score: :desc } + scope :ordered_by_comments, -> { order comments_count: :desc } scope :in_bucket, ->(bucket) { where bucket: bucket } - # FIXME: Compute activity and comment count at write time so it's easier to query for. - scope :left_joins_comments, -> do - left_joins(:messages).merge(Message.left_joins_messageable(:comments)) - end - scope :ordered_by_activity, -> do - left_joins_comments.select("bubbles.*, COUNT(comments.id) + bubbles.boost_count AS activity").group(:id).order("activity DESC") - end - scope :ordered_by_comments, -> do - left_joins_comments.select("bubbles.*, COUNT(comments.id) AS comment_count").group(:id).order("comment_count DESC") - end - scope :indexed_by, ->(index) do case index when "most_active" then ordered_by_activity @@ -34,8 +25,8 @@ class Bubble < ApplicationRecord end end - def activity_count - boost_count + messages.comments.size + def rescore + update! activity_score: boost_count + messages.comments.size end private diff --git a/app/models/bubble/boostable.rb b/app/models/bubble/boostable.rb index 0853603bc..92038f9e2 100644 --- a/app/models/bubble/boostable.rb +++ b/app/models/bubble/boostable.rb @@ -8,6 +8,7 @@ module Bubble::Boostable def boost! transaction do increment! :boost_count + rescore track_event :boosted end end diff --git a/app/models/comment.rb b/app/models/comment.rb index 4a9afb28b..f723d28f7 100644 --- a/app/models/comment.rb +++ b/app/models/comment.rb @@ -4,4 +4,14 @@ class Comment < ApplicationRecord belongs_to :creator, class_name: "User", default: -> { Current.user } searchable_by :body, using: :comments_search_index + + def captured_as(message) + message.bubble.increment! :comments_count + message.bubble.rescore + end + + def uncaptured_as(message) + message.bubble.decrement! :comments_count + message.bubble.rescore + end end diff --git a/app/models/concerns/messageable.rb b/app/models/concerns/messageable.rb index 1005f5492..7be25c747 100644 --- a/app/models/concerns/messageable.rb +++ b/app/models/concerns/messageable.rb @@ -7,4 +7,10 @@ module Messageable has_one :message, as: :messageable, touch: true has_one :bubble, through: :message end + + def captured_as(...) + end + + def uncaptured_as(...) + end end diff --git a/app/models/message.rb b/app/models/message.rb index ed7a0b823..8f283829a 100644 --- a/app/models/message.rb +++ b/app/models/message.rb @@ -5,8 +5,15 @@ class Message < ApplicationRecord scope :chronologically, -> { order created_at: :asc, id: :desc } - # FIXME: Will be made redundant when we compute activity and comment count at write time. See commit. - scope :left_joins_messageable, ->(messageable_type) do - joins "LEFT OUTER JOIN #{messageable_type} ON messages.messageable_id = #{messageable_type}.id" - end + after_create :captured + after_destroy :uncaptured + + private + def captured + messageable.captured_as(self) + end + + def uncaptured + messageable.uncaptured_as(self) + end end diff --git a/db/migrate/20241115234456_add_comments_count_to_bubbles.rb b/db/migrate/20241115234456_add_comments_count_to_bubbles.rb new file mode 100644 index 000000000..82140ef9b --- /dev/null +++ b/db/migrate/20241115234456_add_comments_count_to_bubbles.rb @@ -0,0 +1,5 @@ +class AddCommentsCountToBubbles < ActiveRecord::Migration[8.0] + def change + add_column :bubbles, :comments_count, :integer, null: false, default: 0 + end +end diff --git a/db/migrate/20241115234505_add_activity_score_to_bubbles.rb b/db/migrate/20241115234505_add_activity_score_to_bubbles.rb new file mode 100644 index 000000000..df934bd7f --- /dev/null +++ b/db/migrate/20241115234505_add_activity_score_to_bubbles.rb @@ -0,0 +1,5 @@ +class AddActivityScoreToBubbles < ActiveRecord::Migration[8.0] + def change + add_column :bubbles, :activity_score, :integer, null: false, default: 0 + end +end diff --git a/db/schema.rb b/db/schema.rb index a6bac56a8..87b4b4222 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[8.0].define(version: 2024_11_13_185136) do +ActiveRecord::Schema[8.0].define(version: 2024_11_15_234505) do create_table "accesses", force: :cascade do |t| t.integer "bucket_id", null: false t.integer "user_id", null: false @@ -92,6 +92,8 @@ ActiveRecord::Schema[8.0].define(version: 2024_11_13_185136) do t.integer "bucket_id", null: false t.integer "boost_count", default: 0, null: false t.integer "stage_id" + t.integer "comments_count", default: 0, null: false + t.integer "activity_score", default: 0, null: false t.index ["bucket_id"], name: "index_bubbles_on_bucket_id" t.index ["stage_id"], name: "index_bubbles_on_stage_id" end diff --git a/test/fixtures/bubbles.yml b/test/fixtures/bubbles.yml index 7e44c8a85..99a0fa5bc 100644 --- a/test/fixtures/bubbles.yml +++ b/test/fixtures/bubbles.yml @@ -6,6 +6,8 @@ logo: due_on: <%= 3.days.from_now %> created_at: <%= 1.week.ago %> boost_count: 5 + comments_count: 2 + activity_score: 7 layout: bucket: writebook @@ -13,6 +15,8 @@ layout: title: Layout is broken color: "#698F9C" created_at: <%= 1.week.ago %> + comments_count: 1 + activity_score: 1 text: bucket: writebook diff --git a/test/models/bubble_test.rb b/test/models/bubble_test.rb index eae16426a..62b958414 100644 --- a/test/models/bubble_test.rb +++ b/test/models/bubble_test.rb @@ -14,8 +14,10 @@ class BubbleTest < ActiveSupport::TestCase end test "boosting" do - assert_difference %w[ bubbles(:logo).boost_count Event.count ], +1 do - bubbles(:logo).boost! + assert_changes "bubbles(:logo).activity_score", +1 do + assert_difference %w[ bubbles(:logo).boost_count Event.count ], +1 do + bubbles(:logo).boost! + end end end @@ -33,8 +35,8 @@ class BubbleTest < ActiveSupport::TestCase end test "ordering by activity" do - bubbles(:layout).update! boost_count: 1_000 - assert_equal bubbles(:layout, :logo, :shipping, :text), Bubble.ordered_by_activity + bubbles(:layout).tap { |b| b.update!(boost_count: 1_000) }.rescore + assert_equal bubbles(:layout, :logo, :shipping, :text).pluck(:title), Bubble.ordered_by_activity.pluck(:title) end test "ordering by comments" do diff --git a/test/models/comment_test.rb b/test/models/comment_test.rb index d898535b0..b5cbe0779 100644 --- a/test/models/comment_test.rb +++ b/test/models/comment_test.rb @@ -10,4 +10,18 @@ class CommentTest < ActiveSupport::TestCase assert_includes Comment.search("something rustic"), message.comment end + + test "updating bubble counter" do + assert_changes "bubbles(:logo).activity_score" do + assert_difference "bubbles(:logo).comments_count", 1 do + bubbles(:logo).capture Comment.new(body: "I'd prefer something more rustic") + end + end + + assert_changes "bubbles(:logo).activity_score" do + assert_difference "bubbles(:logo).comments_count", -1 do + bubbles(:logo).messages.comments.last.destroy + end + end + end end