Fix n+1 queries in StatusThreadingConcern (#7321)

This commit is contained in:
Eugen Rochko 2018-05-03 10:41:58 +02:00 committed by GitHub
parent a3d84e705a
commit a5293fdf61
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 74 additions and 20 deletions

View file

@ -3,9 +3,10 @@
class StatusFilter class StatusFilter
attr_reader :status, :account attr_reader :status, :account
def initialize(status, account) def initialize(status, account, preloaded_relations = {})
@status = status @status = status
@account = account @account = account
@preloaded_relations = preloaded_relations
end end
def filtered? def filtered?
@ -24,15 +25,15 @@ class StatusFilter
end end
def blocking_account? def blocking_account?
account.blocking? status.account_id @preloaded_relations[:blocking] ? @preloaded_relations[:blocking][status.account_id] : account.blocking?(status.account_id)
end end
def blocking_domain? def blocking_domain?
account.domain_blocking? status.account_domain @preloaded_relations[:domain_blocking_by_domain] ? @preloaded_relations[:domain_blocking_by_domain][status.account_domain] : account.domain_blocking?(status.account_domain)
end end
def muting_account? def muting_account?
account.muting? status.account_id @preloaded_relations[:muting] ? @preloaded_relations[:muting][status.account_id] : account.muting?(status.account_id)
end end
def silenced_account? def silenced_account?
@ -44,7 +45,7 @@ class StatusFilter
end end
def account_following_status_account? def account_following_status_account?
account&.following? status.account_id @preloaded_relations[:following] ? @preloaded_relations[:following][status.account_id] : account&.following?(status.account_id)
end end
def blocked_by_policy? def blocked_by_policy?
@ -52,6 +53,6 @@ class StatusFilter
end end
def policy_allows_show? def policy_allows_show?
StatusPolicy.new(account, status).show? StatusPolicy.new(account, status, @preloaded_relations).show?
end end
end end

View file

@ -20,6 +20,10 @@ module AccountInteractions
follow_mapping(Block.where(target_account_id: target_account_ids, account_id: account_id), :target_account_id) follow_mapping(Block.where(target_account_id: target_account_ids, account_id: account_id), :target_account_id)
end end
def blocked_by_map(target_account_ids, account_id)
follow_mapping(Block.where(account_id: target_account_ids, target_account_id: account_id), :account_id)
end
def muting_map(target_account_ids, account_id) def muting_map(target_account_ids, account_id)
Mute.where(target_account_id: target_account_ids, account_id: account_id).each_with_object({}) do |mute, mapping| Mute.where(target_account_id: target_account_ids, account_id: account_id).each_with_object({}) do |mute, mapping|
mapping[mute.target_account_id] = { mapping[mute.target_account_id] = {
@ -38,8 +42,12 @@ module AccountInteractions
def domain_blocking_map(target_account_ids, account_id) def domain_blocking_map(target_account_ids, account_id)
accounts_map = Account.where(id: target_account_ids).select('id, domain').map { |a| [a.id, a.domain] }.to_h accounts_map = Account.where(id: target_account_ids).select('id, domain').map { |a| [a.id, a.domain] }.to_h
blocked_domains = AccountDomainBlock.where(account_id: account_id, domain: accounts_map.values).pluck(:domain) blocked_domains = domain_blocking_map_by_domain(accounts_map.values.compact, account_id)
accounts_map.map { |id, domain| [id, blocked_domains.include?(domain)] }.to_h accounts_map.map { |id, domain| [id, blocked_domains[domain]] }.to_h
end
def domain_blocking_map_by_domain(target_domains, account_id)
follow_mapping(AccountDomainBlock.where(account_id: account_id, domain: target_domains), :domain)
end end
private private

View file

@ -71,10 +71,21 @@ module StatusThreadingConcern
end end
def find_statuses_from_tree_path(ids, account) def find_statuses_from_tree_path(ids, account)
statuses = statuses_with_accounts(ids).to_a statuses = statuses_with_accounts(ids).to_a
account_ids = statuses.map(&:account_id).uniq
domains = statuses.map(&:account_domain).compact.uniq
# FIXME: n+1 bonanza relations = if account.present?
statuses.reject! { |status| filter_from_context?(status, account) } {
blocking: Account.blocking_map(account_ids, account.id),
blocked_by: Account.blocked_by_map(account_ids, account.id),
muting: Account.muting_map(account_ids, account.id),
following: Account.following_map(account_ids, account.id),
domain_blocking_by_domain: Account.domain_blocking_map_by_domain(domains, account.id),
}
end
statuses.reject! { |status| filter_from_context?(status, account, relations) }
# Order ancestors/descendants by tree path # Order ancestors/descendants by tree path
statuses.sort_by! { |status| ids.index(status.id) } statuses.sort_by! { |status| ids.index(status.id) }
@ -84,7 +95,7 @@ module StatusThreadingConcern
Status.where(id: ids).includes(:account) Status.where(id: ids).includes(:account)
end end
def filter_from_context?(status, account) def filter_from_context?(status, account, relations)
StatusFilter.new(status, account).filtered? StatusFilter.new(status, account, relations).filtered?
end end
end end

View file

@ -1,26 +1,32 @@
# frozen_string_literal: true # frozen_string_literal: true
class StatusPolicy < ApplicationPolicy class StatusPolicy < ApplicationPolicy
def initialize(current_account, record, preloaded_relations = {})
super(current_account, record)
@preloaded_relations = preloaded_relations
end
def index? def index?
staff? staff?
end end
def show? def show?
if direct? if direct?
owned? || record.mentions.where(account: current_account).exists? owned? || mention_exists?
elsif private? elsif private?
owned? || current_account&.following?(author) || record.mentions.where(account: current_account).exists? owned? || following_author? || mention_exists?
else else
current_account.nil? || !author.blocking?(current_account) current_account.nil? || !author_blocking?
end end
end end
def reblog? def reblog?
!direct? && (!private? || owned?) && show? && !current_account&.blocking?(author) !direct? && (!private? || owned?) && show? && !blocking_author?
end end
def favourite? def favourite?
show? && !current_account&.blocking?(author) show? && !blocking_author?
end end
def destroy? def destroy?
@ -47,6 +53,34 @@ class StatusPolicy < ApplicationPolicy
record.private_visibility? record.private_visibility?
end end
def mention_exists?
return false if current_account.nil?
if record.mentions.loaded?
record.mentions.any? { |mention| mention.account_id == current_account.id }
else
record.mentions.where(account: current_account).exists?
end
end
def blocking_author?
return false if current_account.nil?
@preloaded_relations[:blocking] ? @preloaded_relations[:blocking][author.id] : current_account.blocking?(author)
end
def author_blocking?
return false if current_account.nil?
@preloaded_relations[:blocked_by] ? @preloaded_relations[:blocked_by][author.id] : author.blocking?(current_account)
end
def following_author?
return false if current_account.nil?
@preloaded_relations[:following] ? @preloaded_relations[:following][author.id] : current_account.following?(author)
end
def author def author
record.account record.account
end end