Add feed variants with shuffle post-ranking (#6766) [deploy]

* Add feed variants with shuffle post-ranking

* Refactor tests and fix erroneous shuffle
This commit is contained in:
Ben Halpern 2020-03-23 18:09:30 -04:00 committed by GitHub
parent 3187514ad6
commit da8dbd2180
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 75 additions and 62 deletions

View file

@ -39,6 +39,12 @@ class Stories::FeedsController < ApplicationController
feed.more_comments_experiment
when "more_experience_level_weight_experiment"
feed.more_experience_level_weight_experiment
when "more_tag_weight_randomized_at_end_experiment"
feed.more_tag_weight_randomized_at_end_experiment
when "more_experience_level_weight_randomized_at_end_experiment"
feed.more_experience_level_weight_randomized_at_end_experiment
when "more_comments_randomized_at_end_experiment"
feed.more_comments_randomized_at_end_experiment
when "mix_of_everything_experiment" # mix of all experiments. New experiments also added. This is the "index fund" version.
feed.mix_of_everything_experiment
else

View file

@ -1,6 +1,6 @@
module Articles
class Feed
RANDOM_OFFSET_VALUES = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 3, 3, 4, 5, 6, 7, 8, 9, 10, 11].freeze
RANDOM_OFFSET_VALUES = [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 3, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13].freeze
def initialize(user: nil, number_of_articles: 35, page: 1, tag: nil)
@user = user
@ -100,7 +100,7 @@ module Articles
end
def mix_of_everything_experiment
case rand(7)
case rand(10)
when 0
default_home_feed(user_signed_in: true)
when 1
@ -115,11 +115,40 @@ module Articles
more_comments_experiment
when 6
more_experience_level_weight_experiment
when 7
more_tag_weight_randomized_at_end_experiment
when 8
more_experience_level_weight_randomized_at_end_experiment
when 9
more_comments_randomized_at_end_experiment
else
default_home_feed(user_signed_in: true)
end
end
# Randomized at end group for next three experiments
# Rather than randomizing *during* the ranking, rank solely by quality/match
# and randomize the top half of the ranked results.
# Resulting in more relevance and still more freshness.
def more_tag_weight_randomized_at_end_experiment
@randomness = 0
results = more_tag_weight_experiment
first_half(results).shuffle + last_half(results)
end
def more_experience_level_weight_randomized_at_end_experiment
@randomness = 0
results = more_experience_level_weight_experiment
first_half(results).shuffle + last_half(results)
end
def more_comments_randomized_at_end_experiment
@randomness = 0
results = more_comments_experiment
first_half(results).shuffle + last_half(results)
end
def rank_and_sort_articles(articles)
ranked_articles = articles.each_with_object({}) do |article, result|
article_points = score_single_article(article)
@ -176,7 +205,7 @@ module Articles
def globally_hot_articles(user_signed_in)
hot_stories = published_articles_by_tag.
where("score > ? OR featured = ?", 8, true).
where("score > ? OR featured = ?", 7, true).
order("hotness_score DESC")
featured_story = hot_stories.where.not(main_image: nil).first
if user_signed_in
@ -203,5 +232,13 @@ module Articles
def user_following_users_ids
@user_following_users_ids ||= (@user&.cached_following_users_ids || [])
end
def first_half(array)
array[0...(array.length / 2)]
end
def last_half(array)
array[(array.length / 2)..array.length]
end
end
end

View file

@ -9,15 +9,21 @@ experiments:
- more_comments_experiment
- more_experience_level_weight_experiment
- mix_of_everything_experiment
- more_tag_weight_randomized_at_end_experiment
- more_experience_level_weight_randomized_at_end_experiment
- more_comments_randomized_at_end_experiment
weights:
- 25
- 5
- 10
- 10
- 15
- 5
- 5
- 10
- 10
- 10
- 5
- 10
- 5
goals:
- user_creates_comment
- user_creates_reaction

View file

@ -1,5 +1,18 @@
require "rails_helper"
NON_DEFAULT_EXPERIMENTS = %i[
default_home_feed_with_more_randomness_experiment
mix_default_and_more_random_experiment
more_tag_weight_experiment
more_tag_weight_more_random_experiment
more_comments_experiment
more_experience_level_weight_experiment
more_tag_weight_randomized_at_end_experiment
more_experience_level_weight_randomized_at_end_experiment
more_comments_randomized_at_end_experiment
mix_of_everything_experiment
].freeze
RSpec.describe Articles::Feed, type: :service do
let(:user) { create(:user) }
let!(:feed) { described_class.new(user: user, number_of_articles: 100, page: 1) }
@ -137,63 +150,14 @@ RSpec.describe Articles::Feed, type: :service do
end
end
describe "#default_home_feed_with_more_randomness_experiment" do
let!(:new_story) { create(:article, published_at: 10.minutes.ago, score: 10) }
let(:stories) { feed.default_home_feed_with_more_randomness_experiment }
it "includes stories" do
expect(stories).to include(old_story)
expect(stories).to include(new_story)
end
end
describe "#mix_default_and_more_random_experiment" do
let!(:new_story) { create(:article, published_at: 10.minutes.ago, score: 10) }
let(:stories) { feed.mix_default_and_more_random_experiment }
it "includes stories" do
expect(stories).to include(old_story)
expect(stories).to include(new_story)
end
end
describe "#more_tag_weight_experiment" do
let!(:new_story) { create(:article, published_at: 10.minutes.ago, score: 10) }
let(:stories) { feed.more_tag_weight_experiment }
it "includes stories" do
expect(stories).to include(old_story)
expect(stories).to include(new_story)
end
end
describe "#more_experience_level_weight_experiment" do
let!(:new_story) { create(:article, published_at: 10.minutes.ago, score: 10) }
let(:stories) { feed.more_experience_level_weight_experiment }
it "includes stories" do
expect(stories).to include(old_story)
expect(stories).to include(new_story)
end
end
describe "#more_tag_weight_more_random_experiment" do
let!(:new_story) { create(:article, published_at: 10.minutes.ago, score: 10) }
let(:stories) { feed.more_tag_weight_more_random_experiment }
it "includes stories" do
expect(stories).to include(old_story)
expect(stories).to include(new_story)
end
end
describe "#mix_of_everything_experiment" do
let!(:new_story) { create(:article, published_at: 10.minutes.ago, score: 10) }
let(:stories) { feed.mix_of_everything_experiment }
it "includes stories" do
expect(stories).to include(old_story)
expect(stories).to include(new_story)
describe "all non-default experiments" do
it "returns articles for all experiments" do
new_story = create(:article, published_at: 10.minutes.ago, score: 10)
NON_DEFAULT_EXPERIMENTS.each do |method|
stories = feed.default_home_feed_with_more_randomness_experiment
expect(stories).to include(old_story)
expect(stories).to include(new_story)
end
end
end