090ca9c33e
Search query is especially slow if a user searches a generic string which matches many records, in such case search can take tens of seconds or time out. To speed up the search query, we search only for first 1000 records, if there is >1000 matching records we just display "1000+" instead of precise total count supposing that with such amount the exact count is not so important for the user. Because for issues even limited search was not fast enough, 2-phase approach is used for issues: first we use simpler/faster query to get all public issues, if this exceeds the limit, we just return the limit. If the amount of matching results is lower than limit, we re-run more complex search query (which includes also confidential issues). Re-running the complex query should be fast enough in such case because the amount of matching issues is lower than limit. Because exact total_count is now limited, this patch also switches to to "prev/next" pagination. Related #40540
164 lines
4.4 KiB
Ruby
164 lines
4.4 KiB
Ruby
module Gitlab
|
|
class SearchResults
|
|
class FoundBlob
|
|
attr_reader :id, :filename, :basename, :ref, :startline, :data
|
|
|
|
def initialize(opts = {})
|
|
@id = opts.fetch(:id, nil)
|
|
@filename = opts.fetch(:filename, nil)
|
|
@basename = opts.fetch(:basename, nil)
|
|
@ref = opts.fetch(:ref, nil)
|
|
@startline = opts.fetch(:startline, nil)
|
|
@data = opts.fetch(:data, nil)
|
|
end
|
|
|
|
def path
|
|
filename
|
|
end
|
|
|
|
def no_highlighting?
|
|
false
|
|
end
|
|
end
|
|
|
|
attr_reader :current_user, :query
|
|
|
|
# Limit search results by passed projects
|
|
# It allows us to search only for projects user has access to
|
|
attr_reader :limit_projects
|
|
|
|
# Whether a custom filter is used to restrict scope of projects.
|
|
# If the default filter (which lists all projects user has access to)
|
|
# is used, we can skip it when filtering merge requests and optimize the
|
|
# query
|
|
attr_reader :default_project_filter
|
|
|
|
def initialize(current_user, limit_projects, query, default_project_filter: false)
|
|
@current_user = current_user
|
|
@limit_projects = limit_projects || Project.all
|
|
@query = query
|
|
@default_project_filter = default_project_filter
|
|
end
|
|
|
|
def objects(scope, page = nil, without_count = true)
|
|
collection = case scope
|
|
when 'projects'
|
|
projects.page(page).per(per_page)
|
|
when 'issues'
|
|
issues.page(page).per(per_page)
|
|
when 'merge_requests'
|
|
merge_requests.page(page).per(per_page)
|
|
when 'milestones'
|
|
milestones.page(page).per(per_page)
|
|
else
|
|
Kaminari.paginate_array([]).page(page).per(per_page)
|
|
end
|
|
|
|
without_count ? collection.without_count : collection
|
|
end
|
|
|
|
def projects_count
|
|
@projects_count ||= projects.count
|
|
end
|
|
|
|
def issues_count
|
|
@issues_count ||= issues.count
|
|
end
|
|
|
|
def merge_requests_count
|
|
@merge_requests_count ||= merge_requests.count
|
|
end
|
|
|
|
def milestones_count
|
|
@milestones_count ||= milestones.count
|
|
end
|
|
|
|
def limited_projects_count
|
|
@limited_projects_count ||= projects.limit(count_limit).count
|
|
end
|
|
|
|
def limited_issues_count
|
|
return @limited_issues_count if @limited_issues_count
|
|
|
|
# By default getting limited count (e.g. 1000+) is fast on issuable
|
|
# collections except for issues, where filtering both not confidential
|
|
# and confidential issues user has access to, is too complex.
|
|
# It's faster to try to fetch all public issues first, then only
|
|
# if necessary try to fetch all issues.
|
|
sum = issues(public_only: true).limit(count_limit).count
|
|
@limited_issues_count = sum < count_limit ? issues.limit(count_limit).count : sum
|
|
end
|
|
|
|
def limited_merge_requests_count
|
|
@limited_merge_requests_count ||= merge_requests.limit(count_limit).count
|
|
end
|
|
|
|
def limited_milestones_count
|
|
@limited_milestones_count ||= milestones.limit(count_limit).count
|
|
end
|
|
|
|
def single_commit_result?
|
|
false
|
|
end
|
|
|
|
def count_limit
|
|
1001
|
|
end
|
|
|
|
private
|
|
|
|
def projects
|
|
limit_projects.search(query)
|
|
end
|
|
|
|
def issues(finder_params = {})
|
|
issues = IssuesFinder.new(current_user, finder_params).execute
|
|
unless default_project_filter
|
|
issues = issues.where(project_id: project_ids_relation)
|
|
end
|
|
|
|
issues =
|
|
if query =~ /#(\d+)\z/
|
|
issues.where(iid: $1)
|
|
else
|
|
issues.full_search(query)
|
|
end
|
|
|
|
issues.reorder('updated_at DESC')
|
|
end
|
|
|
|
def milestones
|
|
milestones = Milestone.where(project_id: project_ids_relation)
|
|
milestones = milestones.search(query)
|
|
milestones.reorder('updated_at DESC')
|
|
end
|
|
|
|
def merge_requests
|
|
merge_requests = MergeRequestsFinder.new(current_user).execute
|
|
unless default_project_filter
|
|
merge_requests = merge_requests.in_projects(project_ids_relation)
|
|
end
|
|
|
|
merge_requests =
|
|
if query =~ /[#!](\d+)\z/
|
|
merge_requests.where(iid: $1)
|
|
else
|
|
merge_requests.full_search(query)
|
|
end
|
|
|
|
merge_requests.reorder('updated_at DESC')
|
|
end
|
|
|
|
def default_scope
|
|
'projects'
|
|
end
|
|
|
|
def per_page
|
|
20
|
|
end
|
|
|
|
def project_ids_relation
|
|
limit_projects.select(:id).reorder(nil)
|
|
end
|
|
end
|
|
end
|