~cytrogen/masto-fe

2b45fecde1cff0088c3c22095070302baf795a78 — Claire 2 years ago 675672f
Fix multiple N+1s in ConversationsController (#25134)

2 files changed, 43 insertions(+), 12 deletions(-)

M app/controllers/api/v1/conversations_controller.rb
M app/models/account_conversation.rb
M app/controllers/api/v1/conversations_controller.rb => app/controllers/api/v1/conversations_controller.rb +15 -2
@@ 11,7 11,7 @@ class Api::V1::ConversationsController < Api::BaseController

  def index
    @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

  def read


@@ 32,7 32,20 @@ class Api::V1::ConversationsController < Api::BaseController

  def paginated_conversations
    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

  def insert_pagination_headers

M app/models/account_conversation.rb => app/models/account_conversation.rb +28 -10
@@ 17,6 17,8 @@
class AccountConversation < ApplicationRecord
  include Redisable

  attr_writer :participant_accounts

  before_validation :set_last_status
  after_commit :push_to_streaming_api



@@ 26,24 28,40 @@ class AccountConversation < ApplicationRecord

  def participant_account_ids=(arr)
    self[:participant_account_ids] = arr.sort
    @participant_accounts = nil
  end

  def participant_accounts
    if participant_account_ids.empty?
      [account]
    else
      participants = Account.where(id: participant_account_ids)
      participants.empty? ? [account] : participants
    @participant_accounts ||= begin
      if participant_account_ids.empty?
        [account]
      else
        participants = Account.where(id: participant_account_ids).to_a
        participants.empty? ? [account] : participants
      end
    end
  end

  class << self
    def to_a_paginated_by_id(limit, options = {})
      if options[:min_id]
        paginate_by_min_id(limit, options[:min_id], options[:max_id]).reverse
      else
        paginate_by_max_id(limit, options[:max_id], options[:since_id]).to_a
    def to_a_paginated_by_id(limit, min_id: nil, max_id: nil, since_id: nil, preload_participants: true)
      array = begin
        if min_id
          paginate_by_min_id(limit, min_id, max_id).reverse
        else
          paginate_by_max_id(limit, max_id, since_id).to_a
        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)