Revert friends-of-friends follow recommendation query to using a CTE (#29619)

This commit is contained in:
Claire 2024-03-18 13:57:21 +01:00 committed by GitHub
parent b5115850bb
commit d5063072c3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 35 additions and 14 deletions

View file

@ -7,14 +7,35 @@ class AccountSuggestions::FriendsOfFriendsSource < AccountSuggestions::Source
end end
def source_query(account, limit: DEFAULT_LIMIT) def source_query(account, limit: DEFAULT_LIMIT)
first_degree = account.following.where.not(hide_collections: true).select(:id).reorder(nil) Account.find_by_sql([<<~SQL.squish, { id: account.id, limit: limit }]).map { |row| [row.id, row.frequency, row.followers_count] }
base_account_scope(account) WITH first_degree AS (
.joins(:account_stat) SELECT target_account_id
.joins(:passive_relationships).where(passive_relationships: { account_id: first_degree }) FROM follows
.group('accounts.id, account_stats.id') JOIN accounts AS target_accounts ON follows.target_account_id = target_accounts.id
.reorder(frequency: :desc, followers_count: :desc) WHERE account_id = :id
.limit(limit) AND NOT target_accounts.hide_collections
.pluck(Arel.sql('accounts.id, COUNT(*) AS frequency, followers_count')) )
SELECT accounts.id, COUNT(*) AS frequency, account_stats.followers_count as followers_count
FROM accounts
JOIN follows ON follows.target_account_id = accounts.id
JOIN account_stats ON account_stats.account_id = accounts.id
LEFT OUTER JOIN follow_recommendation_mutes ON follow_recommendation_mutes.target_account_id = accounts.id AND follow_recommendation_mutes.account_id = :id
WHERE follows.account_id IN (SELECT * FROM first_degree)
AND NOT EXISTS (SELECT 1 FROM blocks b WHERE b.target_account_id = follows.target_account_id AND b.account_id = :id)
AND NOT EXISTS (SELECT 1 FROM blocks b WHERE b.target_account_id = :id AND b.account_id = follows.target_account_id)
AND NOT EXISTS (SELECT 1 FROM mutes m WHERE m.target_account_id = follows.target_account_id AND m.account_id = :id)
AND (accounts.domain IS NULL OR NOT EXISTS (SELECT 1 FROM account_domain_blocks b WHERE b.account_id = :id AND b.domain = accounts.domain))
AND NOT EXISTS (SELECT 1 FROM follows f WHERE f.target_account_id = follows.target_account_id AND f.account_id = :id)
AND follows.target_account_id <> :id
AND accounts.discoverable
AND accounts.suspended_at IS NULL
AND accounts.silenced_at IS NULL
AND accounts.moved_to_account_id IS NULL
AND follow_recommendation_mutes.target_account_id IS NULL
GROUP BY accounts.id, account_stats.id
ORDER BY frequency DESC, account_stats.followers_count ASC
LIMIT :limit
SQL
end end
private private

View file

@ -76,19 +76,19 @@ RSpec.describe AccountSuggestions::FriendsOfFriendsSource do
it 'contains correct underlying source data' do it 'contains correct underlying source data' do
expect(source_query_values) expect(source_query_values)
.to contain_exactly( .to contain_exactly(
[eugen.id, 2, 3], # Followed by 2 friends of bob (eve, mallory), 3 followers total (breaks tie) [john.id, 2, 2], # Followed by 2 friends of bob (eve, mallory), 2 followers total (breaks tie)
[john.id, 2, 2], # Followed by 2 friends of bob (eve, mallory), 2 followers total [eugen.id, 2, 3], # Followed by 2 friends of bob (eve, mallory), 3 followers total
[neil.id, 1, 2], # Followed by 1 friends of bob (mallory), 2 followers total (breaks tie) [jerk.id, 1, 1], # Followed by 1 friends of bob (eve), 1 followers total (breaks tie)
[jerk.id, 1, 1] # Followed by 1 friends of bob (eve), 1 followers total [neil.id, 1, 2] # Followed by 1 friends of bob (mallory), 2 followers total
) )
end end
def expected_results def expected_results
[ [
[eugen.id, :friends_of_friends],
[john.id, :friends_of_friends], [john.id, :friends_of_friends],
[neil.id, :friends_of_friends], [eugen.id, :friends_of_friends],
[jerk.id, :friends_of_friends], [jerk.id, :friends_of_friends],
[neil.id, :friends_of_friends],
] ]
end end