Commit ebe4e696 authored by Heinrich Lee Yu's avatar Heinrich Lee Yu

Require a minimum search length on explore page

This page searches all public projects and the trigram index does not
work well with short search terms
parent 10b14f85
...@@ -8,6 +8,8 @@ class Explore::ProjectsController < Explore::ApplicationController ...@@ -8,6 +8,8 @@ class Explore::ProjectsController < Explore::ApplicationController
include SortingHelper include SortingHelper
include SortingPreference include SortingPreference
MIN_SEARCH_LENGTH = 3
before_action :set_non_archived_param before_action :set_non_archived_param
before_action :set_sorting before_action :set_sorting
...@@ -72,7 +74,7 @@ class Explore::ProjectsController < Explore::ApplicationController ...@@ -72,7 +74,7 @@ class Explore::ProjectsController < Explore::ApplicationController
def load_projects def load_projects
load_project_counts load_project_counts
projects = ProjectsFinder.new(current_user: current_user, params: params).execute projects = ProjectsFinder.new(current_user: current_user, params: params.merge(minimum_search_length: MIN_SEARCH_LENGTH)).execute
projects = preload_associations(projects) projects = preload_associations(projects)
projects = projects.page(params[:page]).without_count projects = projects.page(params[:page]).without_count
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
# personal: boolean # personal: boolean
# search: string # search: string
# search_namespaces: boolean # search_namespaces: boolean
# minimum_search_length: int
# non_archived: boolean # non_archived: boolean
# archived: 'only' or boolean # archived: 'only' or boolean
# min_access_level: integer # min_access_level: integer
...@@ -182,6 +183,9 @@ class ProjectsFinder < UnionFinder ...@@ -182,6 +183,9 @@ class ProjectsFinder < UnionFinder
def by_search(items) def by_search(items)
params[:search] ||= params[:name] params[:search] ||= params[:name]
return items.none if params[:search].present? && params[:minimum_search_length].present? && params[:search].length < params[:minimum_search_length].to_i
items.optionally_search(params[:search], include_namespace: params[:search_namespaces].present?) items.optionally_search(params[:search], include_namespace: params[:search_namespaces].present?)
end end
......
= render 'shared/projects/list', projects: projects, user: current_user, explore_page: true, pipeline_status: Feature.enabled?(:dashboard_pipeline_status, default_enabled: true) - if params[:name].present? && params[:name].size < Explore::ProjectsController::MIN_SEARCH_LENGTH
.nothing-here-block
%h5= _('Enter at least three characters to search')
- else
= render 'shared/projects/list', projects: projects, user: current_user, explore_page: true, pipeline_status: Feature.enabled?(:dashboard_pipeline_status, default_enabled: true)
---
title: Require at least 3 characters when searching for project in the Explore page
merge_request:
author:
type: security
...@@ -47,6 +47,14 @@ RSpec.describe 'User explores projects' do ...@@ -47,6 +47,14 @@ RSpec.describe 'User explores projects' do
end end
end end
shared_examples 'minimum search length' do
it 'shows a prompt to enter a longer search term', :js do
fill_in 'name', with: 'z'
expect(page).to have_content('Enter at least three characters to search')
end
end
context 'when viewing public projects' do context 'when viewing public projects' do
before do before do
visit(explore_projects_path) visit(explore_projects_path)
...@@ -54,6 +62,7 @@ RSpec.describe 'User explores projects' do ...@@ -54,6 +62,7 @@ RSpec.describe 'User explores projects' do
include_examples 'shows public and internal projects' include_examples 'shows public and internal projects'
include_examples 'empty search results' include_examples 'empty search results'
include_examples 'minimum search length'
end end
context 'when viewing most starred projects' do context 'when viewing most starred projects' do
...@@ -63,6 +72,7 @@ RSpec.describe 'User explores projects' do ...@@ -63,6 +72,7 @@ RSpec.describe 'User explores projects' do
include_examples 'shows public and internal projects' include_examples 'shows public and internal projects'
include_examples 'empty search results' include_examples 'empty search results'
include_examples 'minimum search length'
end end
context 'when viewing trending projects' do context 'when viewing trending projects' do
...@@ -76,6 +86,7 @@ RSpec.describe 'User explores projects' do ...@@ -76,6 +86,7 @@ RSpec.describe 'User explores projects' do
include_examples 'shows public projects' include_examples 'shows public projects'
include_examples 'empty search results' include_examples 'empty search results'
include_examples 'minimum search length'
end end
end end
end end
......
...@@ -161,6 +161,29 @@ RSpec.describe ProjectsFinder, :do_not_mock_admin_mode do ...@@ -161,6 +161,29 @@ RSpec.describe ProjectsFinder, :do_not_mock_admin_mode do
it { is_expected.to eq([public_project]) } it { is_expected.to eq([public_project]) }
end end
describe 'filter by search with minimum search length' do
context 'when search term is shorter than minimum length' do
let(:params) { { search: 'C', minimum_search_length: 3 } }
it { is_expected.to be_empty }
end
context 'when search term is longer than minimum length' do
let(:project) { create(:project, :public, group: group, name: 'test_project') }
let(:params) { { search: 'test', minimum_search_length: 3 } }
it { is_expected.to eq([project]) }
end
context 'when minimum length is invalid' do
let(:params) { { search: 'C', minimum_search_length: 'x' } }
it 'ignores the minimum length param' do
is_expected.to eq([public_project])
end
end
end
describe 'filter by group name' do describe 'filter by group name' do
let(:params) { { name: group.name, search_namespaces: true } } let(:params) { { name: group.name, search_namespaces: true } }
......
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