From 65da3f259e6102d26b68d0e33a154f5120d0e51c Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Wed, 27 Mar 2024 10:08:31 -0400 Subject: [PATCH] Simplify `Status.tagged_with_all` scope Previously we were looping through all the supplied tag ids and appending a new condition for each one so that the generated query was a collection of AND'd EXISTS statements. Query times under this approach are reasonable with small tag id counts, but grow as the tag id array size grows. The update uses a group by / having approach instead. Query times remain fairly constant under this approach, with new IDs added to an `IN` condition. --- app/models/status.rb | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/app/models/status.rb b/app/models/status.rb index 72a8d6c40e7..80a458929f0 100644 --- a/app/models/status.rb +++ b/app/models/status.rb @@ -113,13 +113,7 @@ class Status < ApplicationRecord scope :tagged_with, ->(tag_ids) { joins(:statuses_tags).where(statuses_tags: { tag_id: tag_ids }) } scope :not_excluded_by_account, ->(account) { where.not(account_id: account.excluded_from_timeline_account_ids) } scope :not_domain_blocked_by_account, ->(account) { account.excluded_from_timeline_domains.blank? ? left_outer_joins(:account) : left_outer_joins(:account).merge(Account.not_domain_blocked_by_account(account)) } - scope :tagged_with_all, lambda { |tag_ids| - Array(tag_ids).map(&:to_i).reduce(self) do |result, id| - result.where(<<~SQL.squish, tag_id: id) - EXISTS(SELECT 1 FROM statuses_tags WHERE statuses_tags.status_id = statuses.id AND statuses_tags.tag_id = :tag_id) - SQL - end - } + scope :tagged_with_all, ->(tag_ids) { joins(:tags).where(tags: { id: tag_ids }).group(:id).having(Arel.star.count.eq tag_ids.size) } scope :tagged_with_none, lambda { |tag_ids| where('NOT EXISTS (SELECT * FROM statuses_tags forbidden WHERE forbidden.status_id = statuses.id AND forbidden.tag_id IN (?))', tag_ids) }