Pre-calculate comment counts and activity scores

This commit is contained in:
Jose Farias
2024-11-15 18:17:42 -06:00
parent bf704d6d0f
commit b4e8adca47
13 changed files with 72 additions and 25 deletions
+1 -1
View File
@@ -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"
+2 -2
View File
@@ -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"
+4 -13
View File
@@ -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
+1
View File
@@ -8,6 +8,7 @@ module Bubble::Boostable
def boost!
transaction do
increment! :boost_count
rescore
track_event :boosted
end
end
+10
View File
@@ -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
+6
View File
@@ -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
+11 -4
View File
@@ -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
@@ -0,0 +1,5 @@
class AddCommentsCountToBubbles < ActiveRecord::Migration[8.0]
def change
add_column :bubbles, :comments_count, :integer, null: false, default: 0
end
end
@@ -0,0 +1,5 @@
class AddActivityScoreToBubbles < ActiveRecord::Migration[8.0]
def change
add_column :bubbles, :activity_score, :integer, null: false, default: 0
end
end
Generated
+3 -1
View File
@@ -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
+4
View File
@@ -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
+6 -4
View File
@@ -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
+14
View File
@@ -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