search: added per-backend sorting fields.
- unified naming to be more user friendly - only sort on supported fields that each backend exposes - fixed few cases of sort errors and added additional tests
This commit is contained in:
parent
cdb5181fd2
commit
e9018c74bd
7 changed files with 74 additions and 21 deletions
|
|
@ -27,6 +27,16 @@ from rhodecode.api.tests.utils import (
|
|||
@pytest.mark.usefixtures("testuser_api", "app")
|
||||
class TestApiSearch(object):
|
||||
|
||||
@pytest.mark.parametrize("sort_dir", [
|
||||
"asc",
|
||||
"desc",
|
||||
])
|
||||
@pytest.mark.parametrize("sort", [
|
||||
"xxx",
|
||||
"author_email",
|
||||
"date",
|
||||
"message",
|
||||
])
|
||||
@pytest.mark.parametrize("query, expected_hits, expected_paths", [
|
||||
('todo', 23, [
|
||||
'vcs/backends/hg/inmemory.py',
|
||||
|
|
@ -55,10 +65,11 @@ class TestApiSearch(object):
|
|||
'vcs/tests/test_cli.py']),
|
||||
('owner:michał test', 0, []),
|
||||
])
|
||||
def test_search_content_results(self, query, expected_hits, expected_paths):
|
||||
def test_search_content_results(self, sort_dir, sort, query, expected_hits, expected_paths):
|
||||
id_, params = build_data(
|
||||
self.apikey_regular, 'search',
|
||||
search_query=query,
|
||||
search_sort='{}:{}'.format(sort_dir, sort),
|
||||
search_type='content')
|
||||
|
||||
response = api_call(self.app, params)
|
||||
|
|
@ -70,6 +81,16 @@ class TestApiSearch(object):
|
|||
for expected_path in expected_paths:
|
||||
assert expected_path in paths
|
||||
|
||||
@pytest.mark.parametrize("sort_dir", [
|
||||
"asc",
|
||||
"desc",
|
||||
])
|
||||
@pytest.mark.parametrize("sort", [
|
||||
"xxx",
|
||||
"date",
|
||||
"file",
|
||||
"size",
|
||||
])
|
||||
@pytest.mark.parametrize("query, expected_hits, expected_paths", [
|
||||
('readme.rst', 3, []),
|
||||
('test*', 75, []),
|
||||
|
|
@ -77,10 +98,11 @@ class TestApiSearch(object):
|
|||
('extension:rst', 48, []),
|
||||
('extension:rst api', 24, []),
|
||||
])
|
||||
def test_search_file_paths(self, query, expected_hits, expected_paths):
|
||||
def test_search_file_paths(self, sort_dir, sort, query, expected_hits, expected_paths):
|
||||
id_, params = build_data(
|
||||
self.apikey_regular, 'search',
|
||||
search_query=query,
|
||||
search_sort='{}:{}'.format(sort_dir, sort),
|
||||
search_type='path')
|
||||
|
||||
response = api_call(self.app, params)
|
||||
|
|
|
|||
|
|
@ -110,9 +110,13 @@ def perform_search(request, tmpl_context, repo_name=None, repo_group_name=None):
|
|||
c.search_tags = search_tags
|
||||
|
||||
direction, sort_field = searcher.get_sort(search_type, search_sort)
|
||||
c.sort = '{}:{}'.format(direction, sort_field)
|
||||
c.sort_tag = sort_field
|
||||
sort_definition = searcher.sort_def(search_type, direction, sort_field)
|
||||
c.sort = ''
|
||||
c.sort_tag = None
|
||||
c.sort_tag_dir = direction
|
||||
if sort_definition:
|
||||
c.sort = '{}:{}'.format(direction, sort_field)
|
||||
c.sort_tag = sort_field
|
||||
|
||||
|
||||
class SearchView(BaseAppView):
|
||||
|
|
|
|||
|
|
@ -89,6 +89,15 @@ class BaseSearcher(object):
|
|||
"""
|
||||
return val
|
||||
|
||||
def sort_def(self, search_type, direction, sort_field):
|
||||
"""
|
||||
Defines sorting for search. This function should decide if for given
|
||||
search_type, sorting can be done with sort_field.
|
||||
|
||||
It also should translate common sort fields into backend specific. e.g elasticsearch
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@staticmethod
|
||||
def get_sort(search_type, search_val):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -99,6 +99,29 @@ class WhooshSearcher(BaseSearcher):
|
|||
query = u'(%s) OR %s' % (query, hashes_or_query)
|
||||
return query
|
||||
|
||||
def sort_def(self, search_type, direction, sort_field):
|
||||
|
||||
if search_type == 'commit':
|
||||
field_defs = {
|
||||
'message': 'message',
|
||||
'date': 'date',
|
||||
'author_email': 'author',
|
||||
}
|
||||
elif search_type == 'path':
|
||||
field_defs = {
|
||||
'file': 'path',
|
||||
'size': 'size',
|
||||
'lines': 'lines',
|
||||
}
|
||||
elif search_type == 'content':
|
||||
# NOTE(dan): content doesn't support any sorting
|
||||
field_defs = {}
|
||||
else:
|
||||
return ''
|
||||
|
||||
if sort_field in field_defs:
|
||||
return field_defs[sort_field]
|
||||
|
||||
def search(self, query, document_type, search_user,
|
||||
repo_name=None, repo_group_name=None,
|
||||
requested_page=1, page_limit=10, sort=None, raise_on_exc=True):
|
||||
|
|
@ -124,21 +147,16 @@ class WhooshSearcher(BaseSearcher):
|
|||
query = qp.parse(safe_unicode(query))
|
||||
log.debug('query: %s (%s)', query, repr(query))
|
||||
|
||||
def sort_def(_direction, _sort_field):
|
||||
field2whoosh = {
|
||||
'message.raw': 'message',
|
||||
'author.email.raw': 'author',
|
||||
}
|
||||
return field2whoosh.get(_sort_field) or _sort_field
|
||||
|
||||
reverse, sorted_by = False, None
|
||||
direction, sort_field = self.get_sort(search_type, sort)
|
||||
if sort_field:
|
||||
if direction == Searcher.DIRECTION_DESC:
|
||||
reverse = True
|
||||
if direction == Searcher.DIRECTION_ASC:
|
||||
reverse = False
|
||||
sorted_by = sort_def(direction, sort_field)
|
||||
sort_definition = self.sort_def(search_type, direction, sort_field)
|
||||
if sort_definition:
|
||||
sorted_by = sort_definition
|
||||
if direction == Searcher.DIRECTION_DESC:
|
||||
reverse = True
|
||||
if direction == Searcher.DIRECTION_ASC:
|
||||
reverse = False
|
||||
|
||||
whoosh_results = self.searcher.search(
|
||||
query, filter=allowed_repos_filter, limit=None,
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@
|
|||
elif c.sort.startswith('desc:'):
|
||||
return c.url_generator(sort='asc:{}'.format(field_name))
|
||||
|
||||
return 'asc:{}'.format(field_name)
|
||||
return c.url_generator(sort='asc:{}'.format(field_name))
|
||||
%>
|
||||
</%def>
|
||||
|
||||
|
|
|
|||
|
|
@ -9,13 +9,13 @@
|
|||
<th>${_('Commit')}</th>
|
||||
<th></th>
|
||||
<th>
|
||||
<a href="${search.field_sort('message.raw')}">${_('Commit message')}</a>
|
||||
<a href="${search.field_sort('message')}">${_('Commit message')}</a>
|
||||
</th>
|
||||
<th>
|
||||
<a href="${search.field_sort('date')}">${_('Age')}</a>
|
||||
<a href="${search.field_sort('date')}">${_('Commit date')}</a>
|
||||
</th>
|
||||
<th>
|
||||
<a href="${search.field_sort('author.email.raw')}">${_('Author')}</a>
|
||||
<a href="${search.field_sort('author_email')}">${_('Author')}</a>
|
||||
</th>
|
||||
</tr>
|
||||
%for entry in c.formatted_results:
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
<tr>
|
||||
<th>${_('Repository')}</th>
|
||||
<th>
|
||||
<a href="${search.field_sort('file.raw')}">${_('File')}</a>
|
||||
<a href="${search.field_sort('file')}">${_('File')}</a>
|
||||
</th>
|
||||
<th>
|
||||
<a href="${search.field_sort('size')}">${_('Size')}</a>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue