gitlab-org--gitlab-foss/app/controllers/concerns/issuable_collections.rb

157 lines
4.4 KiB
Ruby
Raw Normal View History

# frozen_string_literal: true
module IssuableCollections
extend ActiveSupport::Concern
include PaginatedCollection
include SortingHelper
include SortingPreference
include Gitlab::Utils::StrongMemoize
included do
2017-11-07 13:34:12 +00:00
helper_method :finder
end
private
# rubocop:disable Gitlab/ModuleWithInstanceVariables
2017-11-07 13:34:12 +00:00
def set_issuables_index
@issuables = issuables_collection
unless pagination_disabled?
set_pagination
return if redirect_out_of_range(@issuables, @total_pages)
end
if params[:label_name].present? && @project
2017-11-07 13:34:12 +00:00
labels_params = { project_id: @project.id, title: params[:label_name] }
@labels = LabelsFinder.new(current_user, labels_params).execute
end
@users = []
2017-11-07 13:34:12 +00:00
if params[:assignee_id].present?
assignee = User.find_by_id(params[:assignee_id])
@users.push(assignee) if assignee
end
2017-11-07 13:34:12 +00:00
if params[:author_id].present?
author = User.find_by_id(params[:author_id])
@users.push(author) if author
end
end
def set_pagination
@issuables = @issuables.page(params[:page])
@issuables = per_page_for_relative_position if params[:sort] == 'relative_position'
@issuable_meta_data = Gitlab::IssuableMetadata.new(current_user, @issuables).data
@total_pages = issuable_page_count(@issuables)
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
def pagination_disabled?
false
end
# rubocop: disable CodeReuse/ActiveRecord
2017-11-07 13:34:12 +00:00
def issuables_collection
finder.execute.preload(preload_for_collection)
end
# rubocop: enable CodeReuse/ActiveRecord
def issuable_page_count(relation)
page_count_for_relation(relation, finder.row_count)
end
def page_count_for_relation(relation, row_count)
limit = relation.limit_value.to_f
return 1 if limit.zero?
(row_count.to_f / limit).ceil
end
# manual / relative_position sorting allows for 100 items on the page
def per_page_for_relative_position
@issuables.per(100) # rubocop:disable Gitlab/ModuleWithInstanceVariables
end
def issuable_finder_for(finder_class)
2018-11-09 14:29:45 +00:00
finder_class.new(current_user, finder_options)
end
# rubocop:disable Gitlab/ModuleWithInstanceVariables
2018-11-09 14:29:45 +00:00
def finder_options
params[:state] = default_state if params[:state].blank?
2018-11-09 14:29:45 +00:00
options = {
scope: params[:scope],
state: params[:state],
confidential: Gitlab::Utils.to_boolean(params[:confidential]),
sort: set_sort_order
2018-11-09 14:29:45 +00:00
}
2018-11-09 14:29:45 +00:00
# Used by view to highlight active option
@sort = options[:sort]
2019-06-18 07:45:47 +00:00
# When a user looks for an exact iid, we do not filter by search but only by iid
if params[:search] =~ /^#(?<iid>\d+)\z/
options[:iids] = Regexp.last_match[:iid]
params[:search] = nil
end
if @project
2018-11-09 14:29:45 +00:00
options[:project_id] = @project.id
Extend CTE search optimisation to projects When we use the `search` param on an `IssuableFinder`, we can run into issues. We have trigram indexes to support these searches. On GitLab.com, we often see Postgres's optimiser prioritise the (global) trigram indexes over the index on `project_id`. For group and project searches, we know that it will be quicker to filter by `project_id` first, as it returns fewer rows in most cases. For group issues search, we ran into this issue previously, and went through the following iterations: 1. Use a CTE on the project IDs as an optimisation fence. This prevents the planner from disregarding the index on `project_id`. Unfortunately it breaks some types of sorting, like priority and popularity, as they sort on a joined table. 2. Use a subquery for listing issues, and a CTE for counts. The subquery - in the case of group lists - didn't help as much as the CTE, but was faster than not including it. We can safely use a CTE for counts as they don't have sorting. Now, however, we're seeing the same issue in a project context. The subquery doesn't help at all there (it would only return one row, after all). In an attempt to keep total code complexity under control, this commit removes the subquery optimisation and applies the CTE optimisation only for sorts we know that are safe. This means that for more complicated sorts (like priority and popularity), the search will continue to be very slow. If this is a high-priority issue, we can consider introducing further optimisations, but this finder is already very complicated and additional complexity has a cost. The group CTE optimisation is controlled by the same feature flag as before, `attempt_group_search_optimizations`, which is enabled by default. The new project CTE optimisation is controlled by a new feature flag, `attempt_project_search_optimizations`, which is disabled by default.
2019-04-03 09:46:13 +00:00
options[:attempt_project_search_optimizations] = true
elsif @group
2018-11-09 14:29:45 +00:00
options[:group_id] = @group.id
options[:include_subgroups] = true
options[:attempt_group_search_optimizations] = true
end
2018-11-09 14:29:45 +00:00
params.permit(finder_type.valid_params).merge(options)
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
2018-11-09 14:29:45 +00:00
def default_state
'opened'
end
def legacy_sort_cookie_name
'issuable_sort'
end
def default_sort_order
case params[:state]
2017-09-23 00:46:53 +00:00
when 'opened', 'all' then sort_value_created_date
when 'merged', 'closed' then sort_value_recently_updated
2017-09-23 00:46:53 +00:00
else sort_value_created_date
end
end
2017-11-07 13:34:12 +00:00
def finder
@finder ||= issuable_finder_for(finder_type)
2017-11-07 13:34:12 +00:00
end
def collection_type
@collection_type ||= if finder_type <= IssuesFinder
2017-11-07 13:34:12 +00:00
'Issue'
elsif finder_type <= MergeRequestsFinder
2017-11-07 13:34:12 +00:00
'MergeRequest'
end
end
# rubocop:disable Gitlab/ModuleWithInstanceVariables
2017-11-07 13:34:12 +00:00
def preload_for_collection
common_attributes = [:author, :assignees, :labels, :milestone]
2017-11-07 13:34:12 +00:00
@preload_for_collection ||= case collection_type
when 'Issue'
common_attributes + [:project, project: :namespace]
2017-11-07 13:34:12 +00:00
when 'MergeRequest'
common_attributes + [:target_project, :latest_merge_request_diff, source_project: :route, head_pipeline: :project, target_project: :namespace]
2017-11-07 13:34:12 +00:00
end
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
end
IssuableCollections.prepend_if_ee('EE::IssuableCollections')