From 77ac3b7891d40196b04f41af4da357a95b90d149 Mon Sep 17 00:00:00 2001 From: Jean-Philippe Lang Date: Sun, 5 Jan 2014 14:23:36 +0000 Subject: [PATCH] Reduces the number of subqueries when searching with many custom fields set as searchable (#15781). git-svn-id: http://svn.redmine.org/redmine/trunk@12481 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- app/models/custom_field.rb | 5 +++-- app/models/issue_custom_field.rb | 13 +++++++------ .../acts_as_searchable/lib/acts_as_searchable.rb | 15 ++++++++++----- 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/app/models/custom_field.rb b/app/models/custom_field.rb index b2c54d030..72ac471fd 100644 --- a/app/models/custom_field.rb +++ b/app/models/custom_field.rb @@ -173,17 +173,18 @@ class CustomField < ActiveRecord::Base format.join_for_order_statement(self) end - def visibility_by_project_condition(project_key=nil, user=User.current) + def visibility_by_project_condition(project_key=nil, user=User.current, id_column=nil) if visible? || user.admin? "1=1" elsif user.anonymous? "1=0" else project_key ||= "#{self.class.customized_class.table_name}.project_id" + id_column ||= id "#{project_key} IN (SELECT DISTINCT m.project_id FROM #{Member.table_name} m" + " INNER JOIN #{MemberRole.table_name} mr ON mr.member_id = m.id" + " INNER JOIN #{table_name_prefix}custom_fields_roles#{table_name_suffix} cfr ON cfr.role_id = mr.role_id" + - " WHERE m.user_id = #{user.id} AND cfr.custom_field_id = #{id})" + " WHERE m.user_id = #{user.id} AND cfr.custom_field_id = #{id_column})" end end diff --git a/app/models/issue_custom_field.rb b/app/models/issue_custom_field.rb index 513b96a54..776bb4f0a 100644 --- a/app/models/issue_custom_field.rb +++ b/app/models/issue_custom_field.rb @@ -28,13 +28,14 @@ class IssueCustomField < CustomField super || (roles & user.roles_for_project(project)).present? end - def visibility_by_project_condition(*args) + def visibility_by_project_condition(project_key=nil, user=User.current, id_column=nil) sql = super - additional_sql = "#{Issue.table_name}.tracker_id IN (SELECT tracker_id FROM #{table_name_prefix}custom_fields_trackers#{table_name_suffix} WHERE custom_field_id = #{id})" - unless is_for_all? - additional_sql << " AND #{Issue.table_name}.project_id IN (SELECT project_id FROM #{table_name_prefix}custom_fields_projects#{table_name_suffix} WHERE custom_field_id = #{id})" - end - "((#{sql}) AND (#{additional_sql}))" + id_column ||= id + tracker_condition = "#{Issue.table_name}.tracker_id IN (SELECT tracker_id FROM #{table_name_prefix}custom_fields_trackers#{table_name_suffix} WHERE custom_field_id = #{id_column})" + project_condition = "EXISTS (SELECT 1 FROM #{CustomField.table_name} ifa WHERE ifa.is_for_all = #{connection.quoted_true} AND ifa.id = #{id_column})" + + " OR #{Issue.table_name}.project_id IN (SELECT project_id FROM #{table_name_prefix}custom_fields_projects#{table_name_suffix} WHERE custom_field_id = #{id_column})" + + "((#{sql}) AND (#{tracker_condition}) AND (#{project_condition}))" end def validate_custom_field diff --git a/lib/plugins/acts_as_searchable/lib/acts_as_searchable.rb b/lib/plugins/acts_as_searchable/lib/acts_as_searchable.rb index 3e91c72dc..dbdfa8992 100644 --- a/lib/plugins/acts_as_searchable/lib/acts_as_searchable.rb +++ b/lib/plugins/acts_as_searchable/lib/acts_as_searchable.rb @@ -82,11 +82,16 @@ module Redmine if !options[:titles_only] && searchable_options[:search_custom_fields] searchable_custom_fields = CustomField.where(:type => "#{self.name}CustomField", :searchable => true) - searchable_custom_fields.each do |field| - sql = "#{table_name}.id IN (SELECT customized_id FROM #{CustomValue.table_name}" + - " WHERE customized_type='#{self.name}' AND customized_id=#{table_name}.id AND LOWER(value) LIKE ?" + - " AND #{CustomValue.table_name}.custom_field_id = #{field.id})" + - " AND #{field.visibility_by_project_condition(searchable_options[:project_key], user)}" + fields_by_visibility = searchable_custom_fields.group_by {|field| + field.visibility_by_project_condition(searchable_options[:project_key], user, "cfs.custom_field_id") + } + # only 1 subquery for all custom fields with the same visibility statement + fields_by_visibility.each do |visibility, fields| + ids = fields.map(&:id).join(',') + sql = "#{table_name}.id IN (SELECT cfs.customized_id FROM #{CustomValue.table_name} cfs" + + " WHERE cfs.customized_type='#{self.name}' AND cfs.customized_id=#{table_name}.id AND LOWER(cfs.value) LIKE ?" + + " AND cfs.custom_field_id IN (#{ids})" + + " AND #{visibility})" token_clauses << sql end end