[deploy] Add top comments to articles feed (#6988)

* Add 'top_comments' to article feed

* Only include root comments

* Cache and eagerly load top comments

* Clear top comments cache when article score is updated

* Add articles.top_comments to bullet whitelist

* Update app/models/article.rb

Co-Authored-By: rhymes <rhymesete@gmail.com>

* Remove limit in top_comments association, remove Rails caching of top comments

* Remove unnecessary test

Co-authored-by: rhymes <rhymesete@gmail.com>
This commit is contained in:
Josh Puetz 2020-04-13 09:21:03 -05:00 committed by GitHub
parent 11c7289b34
commit 802e225a07
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 53 additions and 3 deletions

View file

@ -29,6 +29,11 @@ class Article < ApplicationRecord
counter_culture :organization
has_many :comments, as: :commentable, inverse_of: :commentable
has_many :top_comments,
-> { where("comments.score > ? AND ancestry IS NULL", 10).order("comments.score DESC") },
as: :commentable,
inverse_of: :commentable,
class_name: "Comment"
has_many :profile_pins, as: :pinnable, inverse_of: :pinnable
has_many :buffer_updates, dependent: :destroy
has_many :notifications, as: :notifiable, inverse_of: :notifiable, dependent: :delete_all
@ -120,7 +125,8 @@ class Article < ApplicationRecord
:video, :user_id, :organization_id, :video_source_url, :video_code,
:video_thumbnail_url, :video_closed_caption_track_url, :language,
:experience_level_rating, :experience_level_rating_distribution, :cached_user, :cached_organization,
:published_at, :crossposted_at, :boost_states, :description, :reading_time, :video_duration_in_seconds)
:published_at, :crossposted_at, :boost_states, :description, :reading_time, :video_duration_in_seconds,
:last_comment_at)
}
scope :limited_columns_internal_select, lambda {

View file

@ -27,7 +27,7 @@ module Articles
end
def published_articles_by_tag
articles = Article.published.limited_column_select.page(@page).per(@number_of_articles)
articles = Article.published.limited_column_select.includes(:top_comments).page(@page).per(@number_of_articles)
articles = articles.cached_tagged_with(@tag) if @tag.present? # More efficient than tagged_with
articles
end
@ -213,7 +213,7 @@ module Articles
hot_stories = hot_stories.offset(offset)
new_stories = Article.published.
where("score > ?", -15).
limited_column_select.order("published_at DESC").limit(rand(15..80))
limited_column_select.includes(:top_comments).order("published_at DESC").limit(rand(15..80))
hot_stories = hot_stories.to_a + new_stories.to_a
end
[featured_story, hot_stories.to_a]

View file

@ -24,5 +24,6 @@ json.array!(@stories) do |article|
end
json.tag_list article.cached_tag_list_array
json.top_comments article.top_comments
json.extract! article, *article_methods_to_include
end

View file

@ -1,3 +1,4 @@
# rubocop:disable Metrics/BlockLength
# Silence all Ruby 2.7 deprecation warnings
$VERBOSE = nil
@ -126,6 +127,7 @@ Rails.application.configure do
Bullet.add_whitelist(type: :unused_eager_loading, class_name: "ApiSecret", association: :user)
# acts-as-taggable-on has super weird eager loading problems: <https://github.com/mbleigh/acts-as-taggable-on/issues/91>
Bullet.add_whitelist(type: :n_plus_one_query, class_name: "ActsAsTaggableOn::Tagging", association: :tag)
Bullet.add_whitelist(type: :unused_eager_loading, class_name: "Article", association: :top_comments)
# Check if there are any data update scripts to run during startup
if %w[c console runner s server].include?(ENV["COMMAND"])
@ -137,3 +139,4 @@ Rails.application.configure do
end
Rails.application.routes.default_url_options = { host: Rails.application.config.app_domain }
# rubocop:enable Metrics/BlockLength

View file

@ -68,6 +68,7 @@ Rails.application.configure do
Bullet.add_whitelist(type: :unused_eager_loading, class_name: "ApiSecret", association: :user)
# acts-as-taggable-on has super weird eager loading problems: <https://github.com/mbleigh/acts-as-taggable-on/issues/91>
Bullet.add_whitelist(type: :n_plus_one_query, class_name: "ActsAsTaggableOn::Tagging", association: :tag)
Bullet.add_whitelist(type: :unused_eager_loading, class_name: "Article", association: :top_comments)
end
end

View file

@ -845,6 +845,34 @@ RSpec.describe Article, type: :model do
end
end
describe "#top_comments" do
context "when article has comments" do
let(:root_comment) { create(:comment, commentable: article, score: 20) }
let(:child_comment) { create(:comment, commentable: article, score: 20, parent: root_comment) }
before do
root_comment
child_comment
create_list(:comment, 2, commentable: article, score: 20)
article.reload
end
it "returns comments with score greater than 10" do
expect(article.top_comments.first.score).to be > 10
end
it "only includes root comments" do
expect(article.top_comments).not_to include(child_comment)
end
end
context "when article does not have any comments" do
it "retrns empty set if there aren't any top comments" do
expect(article.top_comments).to be_empty
end
end
end
describe "#touch_by_reaction" do
it "reindexes elasticsearch doc" do
sidekiq_assert_enqueued_with(job: Search::IndexToElasticsearchWorker, args: [described_class.to_s, article.search_id]) do

View file

@ -114,6 +114,17 @@ RSpec.describe "Stories::Feeds", type: :request do
end
end
context "when there are highly rated comments" do
let(:comment) { create(:comment, score: 20) }
let(:article) { comment.commentable }
it "renders top comments for the article" do
get "/stories/feed/infinity", headers: headers
expect(response_article["top_comments"]).not_to be_nil
end
end
context "when user is signed in but there's no field_test" do
before do
sign_in user