diff --git a/rhodecode/api/tests/test_fts_search.py b/rhodecode/api/tests/test_fts_search.py --- a/rhodecode/api/tests/test_fts_search.py +++ b/rhodecode/api/tests/test_fts_search.py @@ -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) diff --git a/rhodecode/apps/search/views.py b/rhodecode/apps/search/views.py --- a/rhodecode/apps/search/views.py +++ b/rhodecode/apps/search/views.py @@ -110,9 +110,13 @@ def perform_search(request, tmpl_context 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): diff --git a/rhodecode/lib/index/__init__.py b/rhodecode/lib/index/__init__.py --- a/rhodecode/lib/index/__init__.py +++ b/rhodecode/lib/index/__init__.py @@ -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): """ diff --git a/rhodecode/lib/index/whoosh.py b/rhodecode/lib/index/whoosh.py --- a/rhodecode/lib/index/whoosh.py +++ b/rhodecode/lib/index/whoosh.py @@ -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, diff --git a/rhodecode/templates/search/search.mako b/rhodecode/templates/search/search.mako --- a/rhodecode/templates/search/search.mako +++ b/rhodecode/templates/search/search.mako @@ -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)) %> diff --git a/rhodecode/templates/search/search_commit.mako b/rhodecode/templates/search/search_commit.mako --- a/rhodecode/templates/search/search_commit.mako +++ b/rhodecode/templates/search/search_commit.mako @@ -9,13 +9,13 @@ ${_('Commit')} - ${_('Commit message')} + ${_('Commit message')} - ${_('Age')} + ${_('Commit date')} - ${_('Author')} + ${_('Author')} %for entry in c.formatted_results: diff --git a/rhodecode/templates/search/search_path.mako b/rhodecode/templates/search/search_path.mako --- a/rhodecode/templates/search/search_path.mako +++ b/rhodecode/templates/search/search_path.mako @@ -6,7 +6,7 @@ ${_('Repository')} - ${_('File')} + ${_('File')} ${_('Size')}