Commit 9b58848a authored by Dylan Griffith's avatar Dylan Griffith

Fix performance issue with global search + refactor

A performance regression was introduced in
https://gitlab.com/gitlab-org/gitlab/-/merge_requests/38095#note_412661217
. This reverts the relevant code that was necessarily passing through a
limited set of project IDs for Elasticsearch.

This also takes an extra refactoring step. Since we are passing project
IDs through to this method it no longer has the projects which was
necessary for the `#generic_search_results` method. But this was only
ever used for user search because users are not indexed in Elasticsearch
and as such cannot actually search using Elasticsearch. I decided it
kept things simpler to move this logic further up the calling stack so
we don't need the `Elastic::SearchResults` objects to be delegating back
again to the normal `SearchResults` objects. This is much simpler
because the calling code is already responsible for determining whether
or not Elasticsearch should be used anyway.
parent eb8418a8
......@@ -4,6 +4,7 @@ module Search
module Elasticsearchable
def use_elasticsearch?
return false if params[:basic_search]
return false if params[:scope] == 'users'
::Gitlab::CurrentSettings.search_using_elasticsearch?(scope: elasticsearchable_scope)
end
......
......@@ -14,7 +14,7 @@ module EE
::Gitlab::Elastic::SearchResults.new(
current_user,
params[:search],
projects,
elastic_projects,
public_and_internal_projects: elastic_global,
filters: { state: params[:state] }
)
......@@ -28,6 +28,25 @@ module EE
true
end
def elastic_projects
# For elasticsearch we need the list of projects to be as small as
# possible since they are loaded from the DB and sent in the
# Elasticsearch query. It should only be strictly the project IDs the
# user has been given authorization for. The Elasticsearch query will
# additionally take care of public projects. This behaves differently
# to the searching Postgres case in which this list of projects is
# intended to be all projects that should appear in the results.
strong_memoize(:elastic_projects) do
if current_user&.can_read_all_resources?
:any
elsif current_user
current_user.authorized_projects.pluck(:id) # rubocop: disable CodeReuse/ActiveRecord
else
[]
end
end
end
override :allowed_scopes
def allowed_scopes
return super unless use_elasticsearch?
......
......@@ -15,6 +15,11 @@ module EE
false
end
override :elastic_projects
def elastic_projects
@elastic_projects ||= projects.pluck_primary_key
end
override :execute
def execute
return super unless use_elasticsearch?
......@@ -22,7 +27,7 @@ module EE
::Gitlab::Elastic::GroupSearchResults.new(
current_user,
params[:search],
projects,
elastic_projects,
group: group,
public_and_internal_projects: elastic_global,
filters: { state: params[:state] }
......
---
title: Fix poor performance with global search across entire instance
merge_request: 42437
author:
type: performance
......@@ -6,27 +6,14 @@ module Gitlab
# superclass inside a module, because autoloading can occur in a
# different order between execution environments.
class GroupSearchResults < Gitlab::Elastic::SearchResults
delegate :users, to: :generic_search_results
delegate :limited_users_count, to: :generic_search_results
attr_reader :group, :default_project_filter, :filters
def initialize(current_user, query, limit_projects = nil, group:, public_and_internal_projects: false, default_project_filter: false, filters: {})
def initialize(current_user, query, limit_project_ids = nil, group:, public_and_internal_projects: false, default_project_filter: false, filters: {})
@group = group
@default_project_filter = default_project_filter
@filters = filters
super(current_user, query, limit_projects, public_and_internal_projects: public_and_internal_projects, filters: filters)
end
def generic_search_results
@generic_search_results ||= Gitlab::GroupSearchResults.new(
current_user,
query,
limit_projects,
group: group,
filters: filters
)
super(current_user, query, limit_project_ids, public_and_internal_projects: public_and_internal_projects, filters: filters)
end
end
end
......
......@@ -8,24 +8,11 @@ module Gitlab
class ProjectSearchResults < Gitlab::Elastic::SearchResults
attr_reader :project, :repository_ref, :filters
delegate :users, to: :generic_search_results
delegate :limited_users_count, to: :generic_search_results
def initialize(current_user, query, project:, repository_ref: nil, filters: {})
@project = project
@repository_ref = repository_ref.presence || project.default_branch
super(current_user, query, [project], public_and_internal_projects: false, filters: filters)
end
def generic_search_results
@generic_search_results ||= Gitlab::ProjectSearchResults.new(
current_user,
query,
project: project,
repository_ref: repository_ref,
filters: filters
)
super(current_user, query, [project.id], public_and_internal_projects: false, filters: filters)
end
private
......
......@@ -11,15 +11,12 @@ module Gitlab
# Limit search results by passed projects
# It allows us to search only for projects user has access to
attr_reader :limit_projects
attr_reader :limit_project_ids
delegate :users, to: :generic_search_results
delegate :limited_users_count, to: :generic_search_results
def initialize(current_user, query, limit_projects = nil, public_and_internal_projects: true, filters: {})
def initialize(current_user, query, limit_project_ids = nil, public_and_internal_projects: true, filters: {})
@current_user = current_user
@query = query
@limit_projects = limit_projects
@limit_project_ids = limit_project_ids
@public_and_internal_projects = public_and_internal_projects
@filters = filters
end
......@@ -44,17 +41,11 @@ module Gitlab
wiki_blobs(page: page, per_page: per_page)
when 'commits'
commits(page: page, per_page: per_page, preload_method: preload_method)
when 'users'
users.page(page).per(per_page)
else
Kaminari.paginate_array([])
end
end
def generic_search_results
@generic_search_results ||= Gitlab::SearchResults.new(current_user, query, limit_projects)
end
def formatted_count(scope)
case scope
when 'projects'
......@@ -73,8 +64,6 @@ module Gitlab
merge_requests_count.to_s
when 'milestones'
milestones_count.to_s
when 'users'
generic_search_results.formatted_count('users')
end
end
......@@ -176,20 +165,6 @@ module Gitlab
private
# Convert the `limit_projects` to a list of ids for Elasticsearch
def limit_project_ids
strong_memoize(:limit_project_ids) do
case limit_projects
when :any then :any
when ActiveRecord::Relation
limit_projects.pluck_primary_key if limit_projects.model == Project
when Array
limit_projects.all? { |x| x.is_a?(Project) } ? limit_projects.map(&:id) : []
else []
end
end
end
# Apply some eager loading to the `records` of an ES result object without
# losing pagination information. Also, take advantage of preload method if
# provided by the caller.
......
......@@ -9,7 +9,7 @@ RSpec.describe Gitlab::Elastic::GroupSearchResults, :elastic do
let(:filters) { {} }
let(:query) { '*' }
subject(:results) { described_class.new(user, query, Project.all, group: group, filters: filters) }
subject(:results) { described_class.new(user, query, Project.all.pluck_primary_key, group: group, filters: filters) }
before do
stub_ee_application_setting(elasticsearch_search: true, elasticsearch_indexing: true)
......@@ -45,32 +45,6 @@ RSpec.describe Gitlab::Elastic::GroupSearchResults, :elastic do
end
end
context 'user search' do
let(:query) { guest.username }
before do
expect(Gitlab::GroupSearchResults).to receive(:new).and_call_original
end
it { expect(results.objects('users')).to contain_exactly(guest) }
it { expect(results.limited_users_count).to eq(1) }
describe 'pagination' do
let(:query) {}
let_it_be(:user2) { create(:user).tap { |u| group.add_user(u, Gitlab::Access::REPORTER) } }
it 'returns the correct page of results' do
expect(results.objects('users', page: 1, per_page: 1)).to contain_exactly(user2)
expect(results.objects('users', page: 2, per_page: 1)).to contain_exactly(guest)
end
it 'returns the correct number of results for one page' do
expect(results.objects('users', page: 1, per_page: 2).count).to eq(2)
end
end
end
context 'query performance' do
include_examples 'does not hit Elasticsearch twice for objects and counts', %w|projects notes blobs wiki_blobs commits issues merge_requests milestones|
end
......
......@@ -167,33 +167,6 @@ RSpec.describe Gitlab::Elastic::ProjectSearchResults, :elastic do
end
end
context 'user search' do
let(:query) { project.owner.username }
before do
expect(Gitlab::ProjectSearchResults).to receive(:new).and_call_original
end
it { expect(results.objects('users')).to eq([project.owner]) }
it { expect(results.limited_users_count).to eq(1) }
describe 'pagination' do
let(:query) { }
let_it_be(:user2) { create(:user).tap { |u| project.add_user(u, Gitlab::Access::REPORTER) } }
it 'returns the correct page of results' do
# UsersFinder defaults to order_id_desc, the newer result will be first
expect(results.objects('users', page: 1, per_page: 1)).to eq([user2])
expect(results.objects('users', page: 2, per_page: 1)).to eq([project.owner])
end
it 'returns the correct number of results for one page' do
expect(results.objects('users', page: 1, per_page: 2).count).to eq(2)
end
end
end
context 'query performance' do
let(:project) { create(:project, :public, :repository, :wiki_repo) }
let(:query) { '*' }
......
......@@ -10,12 +10,12 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
let(:user) { create(:user) }
let(:project_1) { create(:project, :public, :repository, :wiki_repo) }
let(:project_2) { create(:project, :public, :repository, :wiki_repo) }
let(:limit_projects) { [project_1] }
let(:limit_project_ids) { [project_1.id] }
describe '#formatted_count' do
using RSpec::Parameterized::TableSyntax
let(:results) { described_class.new(user, 'hello world', limit_projects) }
let(:results) { described_class.new(user, 'hello world', limit_project_ids) }
where(:scope, :count_method, :expected) do
'projects' | :projects_count | '1234'
......@@ -35,15 +35,10 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
expect(results.formatted_count(scope)).to eq(expected)
end
end
it 'delegates to generic_search_results for users' do
expect(results.generic_search_results).to receive(:formatted_count).with('users').and_return('1000+')
expect(results.formatted_count('users')).to eq('1000+')
end
end
shared_examples_for 'a paginated object' do |object_type|
let(:results) { described_class.new(user, 'hello world', limit_projects) }
let(:results) { described_class.new(user, 'hello world', limit_project_ids) }
it 'does not explode when given a page as a string' do
expect { results.objects(object_type, page: "2") }.not_to raise_error
......@@ -135,7 +130,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
it_behaves_like 'a paginated object', 'issues'
it 'lists found issues' do
results = described_class.new(user, 'hello world', limit_projects)
results = described_class.new(user, 'hello world', limit_project_ids)
issues = results.objects('issues')
expect(issues).to contain_exactly(@issue_1, @issue_2)
......@@ -143,14 +138,14 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
end
it 'returns empty list when issues are not found' do
results = described_class.new(user, 'security', limit_projects)
results = described_class.new(user, 'security', limit_project_ids)
expect(results.objects('issues')).to be_empty
expect(results.issues_count).to eq 0
end
it 'lists issue when search by a valid iid' do
results = described_class.new(user, '#2', limit_projects, public_and_internal_projects: false)
results = described_class.new(user, '#2', limit_project_ids, public_and_internal_projects: false)
issues = results.objects('issues')
expect(issues).to contain_exactly(@issue_2)
......@@ -158,7 +153,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
end
it 'can also find an issue by iid without the prefixed #' do
results = described_class.new(user, '2', limit_projects, public_and_internal_projects: false)
results = described_class.new(user, '2', limit_project_ids, public_and_internal_projects: false)
issues = results.objects('issues')
expect(issues).to contain_exactly(@issue_2)
......@@ -166,7 +161,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
end
it 'returns empty list when search by invalid iid' do
results = described_class.new(user, '#222', limit_projects)
results = described_class.new(user, '#222', limit_project_ids)
expect(results.objects('issues')).to be_empty
expect(results.issues_count).to eq 0
......@@ -217,7 +212,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
it_behaves_like 'a paginated object', 'notes'
it 'lists found notes' do
results = described_class.new(user, 'foo', limit_projects)
results = described_class.new(user, 'foo', limit_project_ids)
notes = results.objects('notes')
expect(notes).to include @note_1
......@@ -227,7 +222,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
end
it 'returns empty list when notes are not found' do
results = described_class.new(user, 'security', limit_projects)
results = described_class.new(user, 'security', limit_project_ids)
expect(results.objects('notes')).to be_empty
expect(results.notes_count).to eq 0
......@@ -237,7 +232,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
describe 'confidential issues' do
let(:project_3) { create(:project, :public) }
let(:project_4) { create(:project, :public) }
let(:limit_projects) { [project_1, project_2, project_3] }
let(:limit_project_ids) { [project_1.id, project_2.id, project_3.id] }
let(:author) { create(:user) }
let(:assignee) { create(:user) }
let(:non_member) { create(:user) }
......@@ -259,7 +254,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
let(:query) { 'issue' }
it 'does not list confidential issues for guests' do
results = described_class.new(nil, query, limit_projects)
results = described_class.new(nil, query, limit_project_ids)
issues = results.objects('issues')
expect(issues).to include @issue
......@@ -272,7 +267,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
end
it 'does not list confidential issues for non project members' do
results = described_class.new(non_member, query, limit_projects)
results = described_class.new(non_member, query, limit_project_ids)
issues = results.objects('issues')
expect(issues).to include @issue
......@@ -285,7 +280,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
end
it 'lists confidential issues for author' do
results = described_class.new(author, query, limit_projects)
results = described_class.new(author, query, limit_project_ids)
issues = results.objects('issues')
expect(issues).to include @issue
......@@ -298,7 +293,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
end
it 'lists confidential issues for assignee' do
results = described_class.new(assignee, query, limit_projects)
results = described_class.new(assignee, query, limit_project_ids)
issues = results.objects('issues')
expect(issues).to include @issue
......@@ -314,7 +309,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
project_1.add_developer(member)
project_2.add_developer(member)
results = described_class.new(member, query, limit_projects)
results = described_class.new(member, query, limit_project_ids)
issues = results.objects('issues')
expect(issues).to include @issue
......@@ -327,7 +322,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
end
it 'lists all issues for admin' do
results = described_class.new(admin, query, limit_projects)
results = described_class.new(admin, query, limit_project_ids)
issues = results.objects('issues')
expect(issues).to include @issue
......@@ -344,7 +339,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
let(:query) { '#1' }
it 'does not list confidential issues for guests' do
results = described_class.new(nil, query, limit_projects)
results = described_class.new(nil, query, limit_project_ids)
issues = results.objects('issues')
expect(issues).to include @issue
......@@ -357,7 +352,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
end
it 'does not list confidential issues for non project members' do
results = described_class.new(non_member, query, limit_projects)
results = described_class.new(non_member, query, limit_project_ids)
issues = results.objects('issues')
expect(issues).to include @issue
......@@ -370,7 +365,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
end
it 'lists confidential issues for author' do
results = described_class.new(author, query, limit_projects)
results = described_class.new(author, query, limit_project_ids)
issues = results.objects('issues')
expect(issues).to include @issue
......@@ -383,7 +378,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
end
it 'lists confidential issues for assignee' do
results = described_class.new(assignee, query, limit_projects)
results = described_class.new(assignee, query, limit_project_ids)
issues = results.objects('issues')
expect(issues).to include @issue
......@@ -399,7 +394,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
project_2.add_developer(member)
project_3.add_developer(member)
results = described_class.new(member, query, limit_projects)
results = described_class.new(member, query, limit_project_ids)
issues = results.objects('issues')
expect(issues).to include @issue
......@@ -412,7 +407,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
end
it 'lists all issues for admin' do
results = described_class.new(admin, query, limit_projects)
results = described_class.new(admin, query, limit_project_ids)
issues = results.objects('issues')
expect(issues).to include @issue
......@@ -458,7 +453,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
it_behaves_like 'a paginated object', 'merge_requests'
it 'lists found merge requests' do
results = described_class.new(user, 'hello world', limit_projects, public_and_internal_projects: false)
results = described_class.new(user, 'hello world', limit_project_ids, public_and_internal_projects: false)
merge_requests = results.objects('merge_requests')
expect(merge_requests).to contain_exactly(@merge_request_1, @merge_request_2)
......@@ -466,14 +461,14 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
end
it 'returns empty list when merge requests are not found' do
results = described_class.new(user, 'security', limit_projects)
results = described_class.new(user, 'security', limit_project_ids)
expect(results.objects('merge_requests')).to be_empty
expect(results.merge_requests_count).to eq 0
end
it 'lists merge request when search by a valid iid' do
results = described_class.new(user, '!2', limit_projects, public_and_internal_projects: false)
results = described_class.new(user, '!2', limit_project_ids, public_and_internal_projects: false)
merge_requests = results.objects('merge_requests')
expect(merge_requests).to contain_exactly(@merge_request_2)
......@@ -481,7 +476,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
end
it 'can also find an issue by iid without the prefixed !' do
results = described_class.new(user, '2', limit_projects, public_and_internal_projects: false)
results = described_class.new(user, '2', limit_project_ids, public_and_internal_projects: false)
merge_requests = results.objects('merge_requests')
expect(merge_requests).to contain_exactly(@merge_request_2)
......@@ -489,7 +484,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
end
it 'returns empty list when search by invalid iid' do
results = described_class.new(user, '#222', limit_projects)
results = described_class.new(user, '#222', limit_project_ids)
expect(results.objects('merge_requests')).to be_empty
expect(results.merge_requests_count).to eq 0
......@@ -501,7 +496,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
let!(:closed_result) { create(:merge_request, :closed, source_project: project, title: 'foo closed') }
let(:scope) { 'merge_requests' }
let(:results) { described_class.new(user, 'foo', [project], filters: filters) }
let(:results) { described_class.new(user, 'foo', [project.id], filters: filters) }
include_examples 'search results filtered by state' do
before do
......@@ -538,7 +533,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
ensure_elasticsearch_index!
result = described_class.new(user, 'term', [project])
result = described_class.new(user, 'term', [project.id])
expect(result.issues_count).to eq(2)
expect(result.merge_requests_count).to eq(2)
......@@ -555,13 +550,13 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
end
def search_for(term)
described_class.new(user, term, [project_1]).objects('blobs').map(&:path)
described_class.new(user, term, [project_1.id]).objects('blobs').map(&:path)
end
it_behaves_like 'a paginated object', 'blobs'
it 'finds blobs' do
results = described_class.new(user, 'def', limit_projects)
results = described_class.new(user, 'def', limit_project_ids)
blobs = results.objects('blobs')
expect(blobs.first.data).to include('def')
......@@ -569,7 +564,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
end
it 'finds blobs by prefix search' do
results = described_class.new(user, 'defau*', limit_projects)
results = described_class.new(user, 'defau*', limit_project_ids)
blobs = results.objects('blobs')
expect(blobs.first.data).to include('default')
......@@ -582,18 +577,18 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
project_2.add_reporter(user)
ensure_elasticsearch_index!
results = described_class.new(user, 'def', [project_1])
results = described_class.new(user, 'def', [project_1.id])
expect(results.blobs_count).to eq 5
result_project_ids = results.objects('blobs').map(&:project_id)
expect(result_project_ids.uniq).to eq([project_1.id])
results = described_class.new(user, 'def', [project_1, project_2])
results = described_class.new(user, 'def', [project_1.id, project_2.id])
expect(results.blobs_count).to eq 10
end
it 'returns zero when blobs are not found' do
results = described_class.new(user, 'asdfg', limit_projects)
results = described_class.new(user, 'asdfg', limit_project_ids)
expect(results.blobs_count).to eq 0
end
......@@ -758,7 +753,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
end
describe 'Wikis' do
let(:results) { described_class.new(user, 'term', limit_projects) }
let(:results) { described_class.new(user, 'term', limit_project_ids) }
subject(:wiki_blobs) { results.objects('wiki_blobs') }
......@@ -797,12 +792,12 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
expect(results.wiki_blobs_count).to eq 1
results = described_class.new(user, 'term', [project_1, project_2])
results = described_class.new(user, 'term', [project_1.id, project_2.id])
expect(results.wiki_blobs_count).to eq 2
end
it 'returns zero when wiki blobs are not found' do
results = described_class.new(user, 'asdfg', limit_projects)
results = described_class.new(user, 'asdfg', limit_project_ids)
expect(results.wiki_blobs_count).to eq 0
end
......@@ -811,13 +806,13 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
let(:project_1) { create(:project, :public, :repository, :wiki_disabled) }
context 'search by member' do
let(:limit_projects) { [project_1] }
let(:limit_project_ids) { [project_1.id] }
it { is_expected.to be_empty }
end
context 'search by non-member' do
let(:limit_projects) { [] }
let(:limit_project_ids) { [] }
it { is_expected.to be_empty }
end
......@@ -827,7 +822,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
let(:project_1) { create(:project, :public, :repository, :wiki_private, :wiki_repo) }
context 'search by member' do
let(:limit_projects) { [project_1] }
let(:limit_project_ids) { [project_1.id] }
before do
project_1.add_guest(user)
......@@ -837,7 +832,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
end
context 'search by non-member' do
let(:limit_projects) { [] }
let(:limit_project_ids) { [] }
it { is_expected.to be_empty }
end
......@@ -853,7 +848,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
it_behaves_like 'a paginated object', 'commits'
it 'finds commits' do
results = described_class.new(user, 'add', limit_projects)
results = described_class.new(user, 'add', limit_project_ids)
commits = results.objects('commits')
expect(commits.first.message.downcase).to include("add")
......@@ -866,15 +861,15 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
project_2.add_reporter(user)
ensure_elasticsearch_index!
results = described_class.new(user, 'add', [project_1])
results = described_class.new(user, 'add', [project_1.id])
expect(results.commits_count).to eq 24
results = described_class.new(user, 'add', [project_1, project_2])
results = described_class.new(user, 'add', [project_1.id, project_2.id])
expect(results.commits_count).to eq 48
end
it 'returns zero when commits are not found' do
results = described_class.new(user, 'asdfg', limit_projects)
results = described_class.new(user, 'asdfg', limit_project_ids)
expect(results.commits_count).to eq 0
end
......@@ -885,7 +880,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
let(:private_project1) { create(:project, :private, :repository, :wiki_repo, description: "Private project") }
let(:private_project2) { create(:project, :private, :repository, :wiki_repo, description: "Private project where I'm a member") }
let(:public_project) { create(:project, :public, :repository, :wiki_repo, description: "Public project") }
let(:limit_projects) { [private_project2] }
let(:limit_project_ids) { [private_project2.id] }
before do
private_project2.project_members.create(user: user, access_level: ProjectMember::DEVELOPER)
......@@ -901,7 +896,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
ensure_elasticsearch_index!
# Authenticated search
results = described_class.new(user, 'project', limit_projects)
results = described_class.new(user, 'project', limit_project_ids)
issues = results.objects('issues')
expect(issues).to include issue_1
......@@ -942,7 +937,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
ensure_elasticsearch_index!
projects = user.authorized_projects
results = described_class.new(user, 'project', projects)
results = described_class.new(user, 'project', projects.pluck_primary_key)
milestones = results.objects('milestones')
expect(milestones).to match_array([milestone_1, milestone_3])
......@@ -969,7 +964,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
it 'returns right set of milestones' do
# Authenticated search
projects = user.authorized_projects
results = described_class.new(user, 'project', projects)
results = described_class.new(user, 'project', projects.pluck_primary_key)
milestones = results.objects('milestones')
expect(milestones).to match_array([milestone_1, milestone_3, milestone_4])
......@@ -1050,7 +1045,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
ensure_elasticsearch_index!
# Authenticated search
results = described_class.new(user, 'project', limit_projects)
results = described_class.new(user, 'project', limit_project_ids)
milestones = results.objects('projects')
expect(milestones).to include internal_project
......@@ -1077,7 +1072,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
ensure_elasticsearch_index!
# Authenticated search
results = described_class.new(user, 'project', limit_projects)
results = described_class.new(user, 'project', limit_project_ids)
merge_requests = results.objects('merge_requests')
expect(merge_requests).to include merge_request_1
......@@ -1106,7 +1101,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
it 'finds the right set of wiki blobs' do
# Authenticated search
results = described_class.new(user, 'term', limit_projects)
results = described_class.new(user, 'term', limit_project_ids)
blobs = results.objects('wiki_blobs')
expect(blobs.map(&:project)).to match_array [internal_project, private_project2, public_project]
......@@ -1138,7 +1133,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
ensure_elasticsearch_index!
# Authenticated search
results = described_class.new(user, 'search', limit_projects)
results = described_class.new(user, 'search', limit_project_ids)
commits = results.objects('commits')
expect(commits.map(&:project)).to match_array [internal_project, private_project2, public_project]
......@@ -1170,7 +1165,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
ensure_elasticsearch_index!
# Authenticated search
results = described_class.new(user, 'tesla', limit_projects)
results = described_class.new(user, 'tesla', limit_project_ids)
blobs = results.objects('blobs')
expect(blobs.map(&:project)).to match_array [internal_project, private_project2, public_project]
......@@ -1187,7 +1182,7 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
end
context 'query performance' do
let(:results) { described_class.new(user, 'hello world', limit_projects) }
let(:results) { described_class.new(user, 'hello world', limit_project_ids) }
include_examples 'does not hit Elasticsearch twice for objects and counts', %w|projects notes blobs wiki_blobs commits issues merge_requests milestones|
end
......
......@@ -210,6 +210,41 @@ RSpec.describe Search::GlobalService do
end
end
describe '#elastic_projects' do
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, namespace: group) }
let_it_be(:another_project) { create(:project) }
let_it_be(:non_admin_user) { create_user_from_membership(project, :developer) }
let_it_be(:admin) { create(:admin) }
let(:service) { described_class.new(user, {}) }
let(:elastic_projects) { service.elastic_projects }
context 'when the user is an admin' do
let(:user) { admin }
it 'returns :any' do
expect(elastic_projects).to eq(:any)
end
end
context 'when the user is not an admin' do
let(:user) { non_admin_user }
it 'returns the projects the user has access to' do
expect(elastic_projects).to eq([project.id])
end
end
context 'when there is no user' do
let(:user) { nil }
it 'returns empty array' do
expect(elastic_projects).to eq([])
end
end
end
context 'confidential notes' do
let(:project) { create(:project, :public) }
......
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