Commit 01354296 authored by Yorick Peterse's avatar Yorick Peterse Committed by Robert Speicher

Refactor Gitlab::SearchResults

Instead of plucking IDs this class now uses ActiveRecord::Relation
objects. Plucking IDs is problematic as searching for projects can lead
to a huge amount of IDs being loaded into memory only to be used as an
argument for another query (instead of just using a sub-query).
parent 2cf7f3f4
...@@ -10,9 +10,8 @@ module Search ...@@ -10,9 +10,8 @@ module Search
group = Group.find_by(id: params[:group_id]) if params[:group_id].present? group = Group.find_by(id: params[:group_id]) if params[:group_id].present?
projects = ProjectsFinder.new.execute(current_user) projects = ProjectsFinder.new.execute(current_user)
projects = projects.in_namespace(group.id) if group projects = projects.in_namespace(group.id) if group
project_ids = projects.pluck(:id)
Gitlab::SearchResults.new(project_ids, params[:search]) Gitlab::SearchResults.new(projects, params[:search])
end end
end end
end end
...@@ -2,12 +2,12 @@ module Gitlab ...@@ -2,12 +2,12 @@ module Gitlab
class SearchResults class SearchResults
attr_reader :query attr_reader :query
# Limit search results by passed project ids # Limit search results by passed projects
# It allows us to search only for projects user has access to # It allows us to search only for projects user has access to
attr_reader :limit_project_ids attr_reader :limit_projects
def initialize(limit_project_ids, query) def initialize(limit_projects, query)
@limit_project_ids = limit_project_ids || Project.all @limit_projects = limit_projects || Project.all
@query = Shellwords.shellescape(query) if query.present? @query = Shellwords.shellescape(query) if query.present?
end end
...@@ -27,7 +27,8 @@ module Gitlab ...@@ -27,7 +27,8 @@ module Gitlab
end end
def total_count def total_count
@total_count ||= projects_count + issues_count + merge_requests_count + milestones_count @total_count ||= projects_count + issues_count + merge_requests_count +
milestones_count
end end
def projects_count def projects_count
...@@ -53,27 +54,29 @@ module Gitlab ...@@ -53,27 +54,29 @@ module Gitlab
private private
def projects def projects
Project.where(id: limit_project_ids).search(query) limit_projects.search(query)
end end
def issues def issues
issues = Issue.where(project_id: limit_project_ids) issues = Issue.where(project_id: project_ids_relation)
if query =~ /#(\d+)\z/ if query =~ /#(\d+)\z/
issues = issues.where(iid: $1) issues = issues.where(iid: $1)
else else
issues = issues.full_search(query) issues = issues.full_search(query)
end end
issues.order('updated_at DESC') issues.order('updated_at DESC')
end end
def milestones def milestones
milestones = Milestone.where(project_id: limit_project_ids) milestones = Milestone.where(project_id: project_ids_relation)
milestones = milestones.search(query) milestones = milestones.search(query)
milestones.order('updated_at DESC') milestones.order('updated_at DESC')
end end
def merge_requests def merge_requests
merge_requests = MergeRequest.in_projects(limit_project_ids) merge_requests = MergeRequest.in_projects(project_ids_relation)
if query =~ /[#!](\d+)\z/ if query =~ /[#!](\d+)\z/
merge_requests = merge_requests.where(iid: $1) merge_requests = merge_requests.where(iid: $1)
else else
...@@ -89,5 +92,9 @@ module Gitlab ...@@ -89,5 +92,9 @@ module Gitlab
def per_page def per_page
20 20
end end
def project_ids_relation
limit_projects.select(:id)
end
end end
end end
require 'spec_helper'
describe Gitlab::SearchResults do
let!(:project) { create(:project, name: 'foo') }
let!(:issue) { create(:issue, project: project, title: 'foo') }
let!(:merge_request) do
create(:merge_request, source_project: project, title: 'foo')
end
let!(:milestone) { create(:milestone, project: project, title: 'foo') }
let(:results) { described_class.new(Project.all, 'foo') }
describe '#total_count' do
it 'returns the total amount of search hits' do
expect(results.total_count).to eq(4)
end
end
describe '#projects_count' do
it 'returns the total amount of projects' do
expect(results.projects_count).to eq(1)
end
end
describe '#issues_count' do
it 'returns the total amount of issues' do
expect(results.issues_count).to eq(1)
end
end
describe '#merge_requests_count' do
it 'returns the total amount of merge requests' do
expect(results.merge_requests_count).to eq(1)
end
end
describe '#milestones_count' do
it 'returns the total amount of milestones' do
expect(results.milestones_count).to eq(1)
end
end
describe '#empty?' do
it 'returns true when there are no search results' do
allow(results).to receive(:total_count).and_return(0)
expect(results.empty?).to eq(true)
end
it 'returns false when there are search results' do
expect(results.empty?).to eq(false)
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment