diff --git a/app/models/collection.rb b/app/models/collection.rb index c5082269e0..ac76331659 100644 --- a/app/models/collection.rb +++ b/app/models/collection.rb @@ -64,11 +64,15 @@ class Collection < ApplicationRecord !local? end - def items_for(account = nil) - result = collection_items.with_accounts - result = account == self.account ? result.pending_or_accepted : result.accepted - result = result.not_blocked_by(account) unless account.nil? - result + def items_for(account = nil, include_accounts: false) + @items_for ||= {} + @items_for[account] ||= begin + result = collection_items + result = result.with_accounts if include_accounts + result = account == self.account ? result.pending_or_accepted : result.accepted + result = result.not_blocked_by(account) unless account.nil? + result + end end def tag_name diff --git a/app/models/collection_item.rb b/app/models/collection_item.rb index 8de27950f1..f37e67e8fd 100644 --- a/app/models/collection_item.rb +++ b/app/models/collection_item.rb @@ -43,7 +43,7 @@ class CollectionItem < ApplicationRecord scope :ordered, -> { order(position: :asc) } scope :with_accounts, -> { includes(account: [:account_stat, :user]) } - scope :not_blocked_by, ->(account) { where.not(accounts: { id: account.blocking }) } + scope :not_blocked_by, ->(account) { joins(:account).where.not(accounts: { id: account.blocking }) } scope :local, -> { joins(:collection).merge(Collection.local) } scope :accepted_partial, ->(account) { joins(:account).merge(Account.local).accepted.where(uri: nil, account_id: account.id) } scope :pending_or_accepted, -> { where(state: [:pending, :accepted]) } diff --git a/app/serializers/rest/collection_with_accounts_serializer.rb b/app/serializers/rest/collection_with_accounts_serializer.rb index be0b955022..ea7602776b 100644 --- a/app/serializers/rest/collection_with_accounts_serializer.rb +++ b/app/serializers/rest/collection_with_accounts_serializer.rb @@ -10,6 +10,6 @@ class REST::CollectionWithAccountsSerializer < ActiveModel::Serializer end def accounts - [object.account] + object.collection_items.filter_map(&:account) + [object.account] + object.items_for(current_user&.account, include_accounts: true).map(&:account) end end diff --git a/spec/models/collection_spec.rb b/spec/models/collection_spec.rb index b9a5453642..67b51fdcea 100644 --- a/spec/models/collection_spec.rb +++ b/spec/models/collection_spec.rb @@ -124,6 +124,28 @@ RSpec.describe Collection do expect(subject.items_for(account)).to match_array(accepted_items + [pending_item]) end end + + context 'when `include_accounts` is set to `true`' do + it 'preloads accounts' do + items = subject.items_for(include_accounts: true).to_a + + expect { items.first.account }.to_not execute_queries + end + end + + context 'when called multiple times' do + let(:account) { subject.account } + + it 'memoizes results' do + subject.items_for.to_a + + expect { subject.items_for.to_a }.to_not execute_queries + + expect { subject.items_for(account).to_a }.to execute_queries + + expect { subject.items_for(account).to_a }.to_not execute_queries + end + end end describe '#tag_name=' do