Add a/b test for which comments to show in feed (#20332)
* Add a/b test for which comments to show in feed * Adjust figure * Started at * Move scope placement around * Add tests for different comments_variants passed * Fix typo
This commit is contained in:
parent
6d669c8a5c
commit
ee29318126
6 changed files with 101 additions and 4 deletions
|
|
@ -4,6 +4,8 @@ module Stories
|
|||
|
||||
def show
|
||||
@page = (params[:page] || 1).to_i
|
||||
@comments_variant = field_test(:comments_to_display_20240129, participant: @user)
|
||||
|
||||
@stories = assign_feed_stories
|
||||
|
||||
add_pinned_article
|
||||
|
|
@ -55,7 +57,8 @@ module Stories
|
|||
# weighted strategy has not. I also don't want to alter the
|
||||
# weighted query implementation as it returns a lovely
|
||||
# ActiveRecord::Relation. So this is a compromise.
|
||||
feed.more_comments_minimal_weight_randomized.to_a
|
||||
|
||||
feed.more_comments_minimal_weight_randomized(comments_variant: @comments_variant)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -146,6 +146,42 @@ class Article < ApplicationRecord
|
|||
inverse_of: :commentable,
|
||||
class_name: "Comment"
|
||||
|
||||
has_many :more_inclusive_top_comments,
|
||||
lambda {
|
||||
where(comments: { score: 5.. }, ancestry: nil, hidden_by_commentable_user: false, deleted: false)
|
||||
.order("comments.score" => :desc)
|
||||
},
|
||||
as: :commentable,
|
||||
inverse_of: :commentable,
|
||||
class_name: "Comment"
|
||||
|
||||
has_many :recent_good_comments,
|
||||
lambda {
|
||||
where(comments: { score: 8.. }, ancestry: nil, hidden_by_commentable_user: false, deleted: false)
|
||||
.order("comments.created_at" => :desc)
|
||||
},
|
||||
as: :commentable,
|
||||
inverse_of: :commentable,
|
||||
class_name: "Comment"
|
||||
|
||||
has_many :more_inclusive_recent_good_comments,
|
||||
lambda {
|
||||
where(comments: { score: 5.. }, ancestry: nil, hidden_by_commentable_user: false, deleted: false)
|
||||
.order("comments.created_at" => :desc)
|
||||
},
|
||||
as: :commentable,
|
||||
inverse_of: :commentable,
|
||||
class_name: "Comment"
|
||||
|
||||
has_many :most_inclusive_recent_good_comments,
|
||||
lambda {
|
||||
where(comments: { score: 3.. }, ancestry: nil, hidden_by_commentable_user: false, deleted: false)
|
||||
.order("comments.created_at" => :desc)
|
||||
},
|
||||
as: :commentable,
|
||||
inverse_of: :commentable,
|
||||
class_name: "Comment"
|
||||
|
||||
validates :body_markdown, bytesize: {
|
||||
maximum: 800.kilobytes,
|
||||
too_long: proc { I18n.t("models.article.is_too_long") }
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ module Articles
|
|||
# puts strategy.call.to_sql
|
||||
#
|
||||
# rubocop:disable Layout/LineLength
|
||||
def call(only_featured: false, must_have_main_image: false, limit: default_limit, offset: default_offset, omit_article_ids: [])
|
||||
def call(only_featured: false, must_have_main_image: false, limit: default_limit, offset: default_offset, omit_article_ids: [], comments_variant: default_comments_variant)
|
||||
# rubocop:enable Layout/LineLength
|
||||
|
||||
# These are the variables we'll pass to the SQL statement.
|
||||
|
|
@ -136,10 +136,24 @@ module Articles
|
|||
# goodness of scopes (e.g., limited_column_select) and eager includes.
|
||||
scope = Article.joins(join_fragment)
|
||||
.limited_column_select
|
||||
.includes(top_comments: :user)
|
||||
.includes(:distinct_reaction_categories)
|
||||
.order(config.order_by.to_sql)
|
||||
|
||||
scope = case comments_variant
|
||||
when "top_comments"
|
||||
scope.includes(top_comments: :user)
|
||||
when "more_inclusive_top_comments"
|
||||
scope.includes(more_inclusive_top_comments: :user)
|
||||
when "recent_good_comments"
|
||||
scope.includes(recent_good_comments: :user)
|
||||
when "more_inclusive_recent_good_comments"
|
||||
scope.includes(more_inclusive_recent_good_comments: :user)
|
||||
when "most_inclusive_recent_good_comments"
|
||||
scope.includes(most_inclusive_recent_good_comments: :user)
|
||||
else
|
||||
scope.includes(top_comments: :user) # fallback default
|
||||
end
|
||||
|
||||
if @user.present? && (hidden_tags = @user.cached_antifollowed_tag_names).any?
|
||||
scope = scope.not_cached_tagged_with_any(hidden_tags)
|
||||
end
|
||||
|
|
@ -306,6 +320,10 @@ module Articles
|
|||
(@page.to_i - 1) * default_limit
|
||||
end
|
||||
|
||||
def default_comments_variant
|
||||
"top_comments"
|
||||
end
|
||||
|
||||
# We want to ensure that we're not randomizing someone's feed all the time; and instead aiming
|
||||
# for somewhat repeatable experiences (e.g. I refresh the page it is likely I will have the
|
||||
# same order of pages even though we've randomized things a bit).
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ json.array!(@stories) do |article|
|
|||
json.tag_list article.cached_tag_list_array
|
||||
json.extract! article, *article_methods_to_include
|
||||
|
||||
json.top_comments article.top_comments do |comment|
|
||||
json.top_comments article.public_send(@comments_variant.to_sym) do |comment|
|
||||
comment = comment.decorate
|
||||
json.comment_id comment.id
|
||||
json.extract! comment, :user_id, :published_timestamp, :published_at_int, :safe_processed_html, :path
|
||||
|
|
|
|||
|
|
@ -27,6 +27,33 @@
|
|||
################################################################################
|
||||
################################################################################
|
||||
experiments:
|
||||
comments_to_display_20240129:
|
||||
started_at: 2024-01-29
|
||||
variants:
|
||||
- top_comments
|
||||
- more_inclusive_top_comments
|
||||
- recent_good_comments
|
||||
- more_inclusive_recent_good_comments
|
||||
- most_inclusive_recent_good_comments
|
||||
weights:
|
||||
- 60
|
||||
- 10
|
||||
- 10
|
||||
- 10
|
||||
- 10
|
||||
goals:
|
||||
- user_creates_pageview
|
||||
- user_creates_article_reaction
|
||||
- user_creates_comment
|
||||
- user_views_pages_on_at_least_two_different_days_within_a_week
|
||||
- user_views_pages_on_at_least_four_different_days_within_a_week
|
||||
- user_creates_article_reaction_on_four_different_days_within_a_week
|
||||
- user_creates_comment_on_at_least_four_different_days_within_a_week
|
||||
- user_views_pages_on_at_least_three_different_hours_within_a_day
|
||||
- user_views_pages_on_at_least_four_different_hours_within_a_day
|
||||
- user_views_pages_on_at_least_twelve_different_hours_within_five_days
|
||||
- user_views_pages_on_at_least_nine_different_days_within_two_weeks
|
||||
|
||||
# NOTE: Our feed strategy testing experiment must begin with "feed_strategy"
|
||||
feed_strategy_starting_20240126:
|
||||
started_at: 2024-01-26
|
||||
|
|
|
|||
|
|
@ -58,6 +58,19 @@ RSpec.describe Articles::Feeds::VariantQuery, type: :service do
|
|||
expect(query_call).to be_a(ActiveRecord::Relation)
|
||||
expect(query_call.to_a).not_to include(article)
|
||||
end
|
||||
|
||||
it "returns proper scope when passed the different test values" do
|
||||
# rubocop:disable Performance/CollectionLiteralInLoop
|
||||
%w[top_comments
|
||||
more_inclusive_top_comments
|
||||
recent_good_comments
|
||||
more_inclusive_recent_good_comments
|
||||
most_inclusive_recent_good_comments].each do |test_value|
|
||||
expect(variant_query.call(comments_variant: test_value).includes_values)
|
||||
.to include(:distinct_reaction_categories, { test_value.to_sym => :user })
|
||||
end
|
||||
# rubocop:enable Performance/CollectionLiteralInLoop
|
||||
end
|
||||
end
|
||||
|
||||
describe "#featured_story_and_default_home_feed" do
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue