Commit 8b7565ba authored by Jonathan Schafer's avatar Jonathan Schafer

Enable search by similarity in ProjectFinder

This will allow the top result to most closely resemble what the user
searches for.
parent aff15d1e
......@@ -209,7 +209,13 @@ class ProjectsFinder < UnionFinder
end
def sort(items)
params[:sort].present? ? items.sort_by_attribute(params[:sort]) : items.projects_order_id_desc
if params[:sort] == 'similarity' && params[:search]
items.sorted_by_similarity_desc(params[:search])
elsif params[:sort].present?
items.sort_by_attribute(params[:sort])
else
items.projects_order_id_desc
end
end
def by_archived(projects)
......
......@@ -13,13 +13,17 @@ module Resolvers
description: 'Search query for project name, path, or description'
argument :ids, [GraphQL::ID_TYPE],
required: false,
description: 'Filter projects by IDs'
required: false,
description: 'Filter projects by IDs'
argument :search_namespaces, GraphQL::BOOLEAN_TYPE,
required: false,
description: 'Include namespace in project search'
argument :sort, GraphQL::STRING_TYPE,
required: false,
description: 'Sort order of results'
def resolve(**args)
ProjectsFinder
.new(current_user: current_user, params: project_finder_params(args), project_ids_relation: parse_gids(args[:ids]))
......@@ -33,7 +37,8 @@ module Resolvers
without_deleted: true,
non_public: params[:membership],
search: params[:search],
search_namespaces: params[:search_namespaces]
search_namespaces: params[:search_namespaces],
sort: params[:sort]
}.compact
end
......
---
title: Prioritize exact matches on project search
merge_request: 43136
author:
type: changed
......@@ -14833,6 +14833,11 @@ type Query {
Include namespace in project search
"""
searchNamespaces: Boolean
"""
Sort order of results
"""
sort: String
): ProjectConnection
"""
......
......@@ -43174,6 +43174,16 @@
},
"defaultValue": null
},
{
"name": "sort",
"description": "Sort order of results",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "after",
"description": "Returns the elements in the list that come after the specified cursor.",
......@@ -6,6 +6,7 @@ query getProjects(
$after: String = ""
$first: Int!
$searchNamespaces: Boolean = false
$sort: String
) {
projects(
search: $search
......@@ -13,6 +14,7 @@ query getProjects(
first: $first
membership: true
searchNamespaces: $searchNamespaces
sort: $sort
) {
nodes {
...Project
......
......@@ -304,9 +304,29 @@ RSpec.describe ProjectsFinder, :do_not_mock_admin_mode do
end
describe 'sorting' do
let(:params) { { sort: 'name_asc' } }
context 'when sorting by a field' do
let(:params) { { sort: 'name_asc' } }
it { is_expected.to eq([internal_project, public_project]) }
it { is_expected.to eq([internal_project, public_project]) }
end
context 'when sorting by similarity' do
let(:params) { { sort: 'similarity', search: 'pro' } }
let_it_be(:internal_project2) do
create(:project, :internal, group: group, name: 'projA', path: 'projA')
end
let_it_be(:internal_project3) do
create(:project, :internal, group: group, name: 'projABC', path: 'projABC')
end
let_it_be(:internal_project4) do
create(:project, :internal, group: group, name: 'projAB', path: 'projAB')
end
it { is_expected.to eq([internal_project2, internal_project4, internal_project3]) }
end
end
describe 'with admin user' do
......
......@@ -15,7 +15,6 @@ RSpec.describe Resolvers::ProjectsResolver do
let_it_be(:group_project) { create(:project, :public, group: group) }
let_it_be(:private_project) { create(:project, :private) }
let_it_be(:other_private_project) { create(:project, :private) }
let_it_be(:other_private_project) { create(:project, :private) }
let_it_be(:private_group_project) { create(:project, :private, group: private_group) }
let_it_be(:user) { create(:user) }
......@@ -117,6 +116,18 @@ RSpec.describe Resolvers::ProjectsResolver do
is_expected.to contain_exactly(project)
end
end
context 'when sort is similarity' do
let_it_be(:named_project1) { create(:project, :public, name: 'projABC', path: 'projABC') }
let_it_be(:named_project2) { create(:project, :public, name: 'projA', path: 'projA') }
let_it_be(:named_project3) { create(:project, :public, name: 'projAB', path: 'projAB') }
let(:filters) { { search: 'projA', sort: 'similarity' } }
it 'returns projects in order of similarity to search' do
is_expected.to match_array([named_project2, named_project3, named_project1])
end
end
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