BigW Consortium Gitlab

Commit 7230a344 by Sean McGivern

Merge branch 'dm-project-search-performance' into 'master'

Drastically improve project search performance by no longer searching namespace name Closes #40510 and #39623 See merge request gitlab-org/gitlab-ce!15590
parents 882dac68 8041a872
...@@ -18,6 +18,7 @@ class Project < ActiveRecord::Base ...@@ -18,6 +18,7 @@ class Project < ActiveRecord::Base
include SelectForProjectAuthorization include SelectForProjectAuthorization
include Routable include Routable
include GroupDescendant include GroupDescendant
include Gitlab::SQL::Pattern
extend Gitlab::ConfigHelper extend Gitlab::ConfigHelper
extend Gitlab::CurrentSettings extend Gitlab::CurrentSettings
...@@ -424,32 +425,17 @@ class Project < ActiveRecord::Base ...@@ -424,32 +425,17 @@ class Project < ActiveRecord::Base
# #
# query - The search query as a String. # query - The search query as a String.
def search(query) def search(query)
ptable = arel_table pattern = to_pattern(query)
ntable = Namespace.arel_table
pattern = "%#{query}%"
# unscoping unnecessary conditions that'll be applied
# when executing `where("projects.id IN (#{union.to_sql})")`
projects = unscoped.select(:id).where(
ptable[:path].matches(pattern)
.or(ptable[:name].matches(pattern))
.or(ptable[:description].matches(pattern))
)
namespaces = unscoped.select(:id)
.joins(:namespace)
.where(ntable[:name].matches(pattern))
union = Gitlab::SQL::Union.new([projects, namespaces])
where("projects.id IN (#{union.to_sql})") # rubocop:disable GitlabSecurity/SqlInjection where(
arel_table[:path].matches(pattern)
.or(arel_table[:name].matches(pattern))
.or(arel_table[:description].matches(pattern))
)
end end
def search_by_title(query) def search_by_title(query)
pattern = "%#{query}%" non_archived.where(arel_table[:name].matches(to_pattern(query)))
table = Project.arel_table
non_archived.where(table[:name].matches(pattern))
end end
def visibility_levels def visibility_levels
......
---
title: Drastically improve project search performance by no longer searching namespace
name
merge_request:
author:
type: performance
...@@ -30,7 +30,7 @@ module Gitlab ...@@ -30,7 +30,7 @@ module Gitlab
def initialize(current_user, limit_projects, query) def initialize(current_user, limit_projects, query)
@current_user = current_user @current_user = current_user
@limit_projects = limit_projects || Project.all @limit_projects = limit_projects || Project.all
@query = Shellwords.shellescape(query) if query.present? @query = query
end end
def objects(scope, page = nil) def objects(scope, page = nil)
......
...@@ -136,7 +136,7 @@ describe Admin::ProjectsFinder do ...@@ -136,7 +136,7 @@ describe Admin::ProjectsFinder do
context 'filter by name' do context 'filter by name' do
let(:params) { { name: 'C' } } let(:params) { { name: 'C' } }
it { is_expected.to match_array([shared_project, public_project, private_project]) } it { is_expected.to match_array([public_project]) }
end end
context 'sorting' do context 'sorting' do
......
...@@ -1254,24 +1254,6 @@ describe Project do ...@@ -1254,24 +1254,6 @@ describe Project do
expect(described_class.search(project.path.upcase)).to eq([project]) expect(described_class.search(project.path.upcase)).to eq([project])
end end
it 'returns projects with a matching namespace name' do
expect(described_class.search(project.namespace.name)).to eq([project])
end
it 'returns projects with a partially matching namespace name' do
expect(described_class.search(project.namespace.name[0..2])).to eq([project])
end
it 'returns projects with a matching namespace name regardless of the casing' do
expect(described_class.search(project.namespace.name.upcase)).to eq([project])
end
it 'returns projects when eager loading namespaces' do
relation = described_class.all.includes(:namespace)
expect(relation.search(project.namespace.name)).to eq([project])
end
describe 'with pending_delete project' do describe 'with pending_delete project' do
let(:pending_delete_project) { create(:project, pending_delete: true) } let(:pending_delete_project) { create(:project, pending_delete: true) }
......
...@@ -35,8 +35,8 @@ describe Search::GlobalService do ...@@ -35,8 +35,8 @@ describe Search::GlobalService do
expect(results.objects('projects')).to match_array [internal_project, public_project] expect(results.objects('projects')).to match_array [internal_project, public_project]
end end
it 'namespace name is searchable' do it 'project name is searchable' do
results = described_class.new(user, search: found_project.namespace.path).execute results = described_class.new(user, search: found_project.name).execute
expect(results.objects('projects')).to match_array [found_project] expect(results.objects('projects')).to match_array [found_project]
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