Fix multiple N+1s in ConversationsController (#25134)

pull/62/head
Claire 2023-06-01 02:41:51 +02:00 committed by GitHub
parent 675672feb6
commit 2b45fecde1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 43 additions and 12 deletions

View File

@ -11,7 +11,7 @@ class Api::V1::ConversationsController < Api::BaseController
def index def index
@conversations = paginated_conversations @conversations = paginated_conversations
render json: @conversations, each_serializer: REST::ConversationSerializer render json: @conversations, each_serializer: REST::ConversationSerializer, relationships: StatusRelationshipsPresenter.new(@conversations.map(&:last_status), current_user&.account_id)
end end
def read def read
@ -32,7 +32,20 @@ class Api::V1::ConversationsController < Api::BaseController
def paginated_conversations def paginated_conversations
AccountConversation.where(account: current_account) AccountConversation.where(account: current_account)
.to_a_paginated_by_id(limit_param(LIMIT), params_slice(:max_id, :since_id, :min_id)) .includes(
account: :account_stat,
last_status: [
:media_attachments,
:preview_cards,
:status_stat,
:tags,
{
active_mentions: [account: :account_stat],
account: :account_stat,
},
]
)
.to_a_paginated_by_id(limit_param(LIMIT), **params_slice(:max_id, :since_id, :min_id))
end end
def insert_pagination_headers def insert_pagination_headers

View File

@ -17,6 +17,8 @@
class AccountConversation < ApplicationRecord class AccountConversation < ApplicationRecord
include Redisable include Redisable
attr_writer :participant_accounts
before_validation :set_last_status before_validation :set_last_status
after_commit :push_to_streaming_api after_commit :push_to_streaming_api
@ -26,26 +28,42 @@ class AccountConversation < ApplicationRecord
def participant_account_ids=(arr) def participant_account_ids=(arr)
self[:participant_account_ids] = arr.sort self[:participant_account_ids] = arr.sort
@participant_accounts = nil
end end
def participant_accounts def participant_accounts
@participant_accounts ||= begin
if participant_account_ids.empty? if participant_account_ids.empty?
[account] [account]
else else
participants = Account.where(id: participant_account_ids) participants = Account.where(id: participant_account_ids).to_a
participants.empty? ? [account] : participants participants.empty? ? [account] : participants
end end
end end
end
class << self class << self
def to_a_paginated_by_id(limit, options = {}) def to_a_paginated_by_id(limit, min_id: nil, max_id: nil, since_id: nil, preload_participants: true)
if options[:min_id] array = begin
paginate_by_min_id(limit, options[:min_id], options[:max_id]).reverse if min_id
paginate_by_min_id(limit, min_id, max_id).reverse
else else
paginate_by_max_id(limit, options[:max_id], options[:since_id]).to_a paginate_by_max_id(limit, max_id, since_id).to_a
end end
end end
if preload_participants
participant_ids = array.flat_map(&:participant_account_ids)
accounts_by_id = Account.where(id: participant_ids).index_by(&:id)
array.each do |conversation|
conversation.participant_accounts = conversation.participant_account_ids.filter_map { |id| accounts_by_id[id] }
end
end
array
end
def paginate_by_min_id(limit, min_id = nil, max_id = nil) def paginate_by_min_id(limit, min_id = nil, max_id = nil)
query = order(arel_table[:last_status_id].asc).limit(limit) query = order(arel_table[:last_status_id].asc).limit(limit)
query = query.where(arel_table[:last_status_id].gt(min_id)) if min_id.present? query = query.where(arel_table[:last_status_id].gt(min_id)) if min_id.present?