Show More
@@ -27,6 +27,16 b' from rhodecode.api.tests.utils import (' | |||||
27 | @pytest.mark.usefixtures("testuser_api", "app") |
|
27 | @pytest.mark.usefixtures("testuser_api", "app") | |
28 | class TestApiSearch(object): |
|
28 | class TestApiSearch(object): | |
29 |
|
29 | |||
|
30 | @pytest.mark.parametrize("sort_dir", [ | |||
|
31 | "asc", | |||
|
32 | "desc", | |||
|
33 | ]) | |||
|
34 | @pytest.mark.parametrize("sort", [ | |||
|
35 | "xxx", | |||
|
36 | "author_email", | |||
|
37 | "date", | |||
|
38 | "message", | |||
|
39 | ]) | |||
30 | @pytest.mark.parametrize("query, expected_hits, expected_paths", [ |
|
40 | @pytest.mark.parametrize("query, expected_hits, expected_paths", [ | |
31 | ('todo', 23, [ |
|
41 | ('todo', 23, [ | |
32 | 'vcs/backends/hg/inmemory.py', |
|
42 | 'vcs/backends/hg/inmemory.py', | |
@@ -55,10 +65,11 b' class TestApiSearch(object):' | |||||
55 | 'vcs/tests/test_cli.py']), |
|
65 | 'vcs/tests/test_cli.py']), | |
56 | ('owner:michał test', 0, []), |
|
66 | ('owner:michał test', 0, []), | |
57 | ]) |
|
67 | ]) | |
58 | def test_search_content_results(self, query, expected_hits, expected_paths): |
|
68 | def test_search_content_results(self, sort_dir, sort, query, expected_hits, expected_paths): | |
59 | id_, params = build_data( |
|
69 | id_, params = build_data( | |
60 | self.apikey_regular, 'search', |
|
70 | self.apikey_regular, 'search', | |
61 | search_query=query, |
|
71 | search_query=query, | |
|
72 | search_sort='{}:{}'.format(sort_dir, sort), | |||
62 | search_type='content') |
|
73 | search_type='content') | |
63 |
|
74 | |||
64 | response = api_call(self.app, params) |
|
75 | response = api_call(self.app, params) | |
@@ -70,6 +81,16 b' class TestApiSearch(object):' | |||||
70 | for expected_path in expected_paths: |
|
81 | for expected_path in expected_paths: | |
71 | assert expected_path in paths |
|
82 | assert expected_path in paths | |
72 |
|
83 | |||
|
84 | @pytest.mark.parametrize("sort_dir", [ | |||
|
85 | "asc", | |||
|
86 | "desc", | |||
|
87 | ]) | |||
|
88 | @pytest.mark.parametrize("sort", [ | |||
|
89 | "xxx", | |||
|
90 | "date", | |||
|
91 | "file", | |||
|
92 | "size", | |||
|
93 | ]) | |||
73 | @pytest.mark.parametrize("query, expected_hits, expected_paths", [ |
|
94 | @pytest.mark.parametrize("query, expected_hits, expected_paths", [ | |
74 | ('readme.rst', 3, []), |
|
95 | ('readme.rst', 3, []), | |
75 | ('test*', 75, []), |
|
96 | ('test*', 75, []), | |
@@ -77,10 +98,11 b' class TestApiSearch(object):' | |||||
77 | ('extension:rst', 48, []), |
|
98 | ('extension:rst', 48, []), | |
78 | ('extension:rst api', 24, []), |
|
99 | ('extension:rst api', 24, []), | |
79 | ]) |
|
100 | ]) | |
80 | def test_search_file_paths(self, query, expected_hits, expected_paths): |
|
101 | def test_search_file_paths(self, sort_dir, sort, query, expected_hits, expected_paths): | |
81 | id_, params = build_data( |
|
102 | id_, params = build_data( | |
82 | self.apikey_regular, 'search', |
|
103 | self.apikey_regular, 'search', | |
83 | search_query=query, |
|
104 | search_query=query, | |
|
105 | search_sort='{}:{}'.format(sort_dir, sort), | |||
84 | search_type='path') |
|
106 | search_type='path') | |
85 |
|
107 | |||
86 | response = api_call(self.app, params) |
|
108 | response = api_call(self.app, params) |
@@ -110,9 +110,13 b' def perform_search(request, tmpl_context' | |||||
110 | c.search_tags = search_tags |
|
110 | c.search_tags = search_tags | |
111 |
|
111 | |||
112 | direction, sort_field = searcher.get_sort(search_type, search_sort) |
|
112 | direction, sort_field = searcher.get_sort(search_type, search_sort) | |
|
113 | sort_definition = searcher.sort_def(search_type, direction, sort_field) | |||
|
114 | c.sort = '' | |||
|
115 | c.sort_tag = None | |||
|
116 | c.sort_tag_dir = direction | |||
|
117 | if sort_definition: | |||
113 | c.sort = '{}:{}'.format(direction, sort_field) |
|
118 | c.sort = '{}:{}'.format(direction, sort_field) | |
114 | c.sort_tag = sort_field |
|
119 | c.sort_tag = sort_field | |
115 | c.sort_tag_dir = direction |
|
|||
116 |
|
120 | |||
117 |
|
121 | |||
118 | class SearchView(BaseAppView): |
|
122 | class SearchView(BaseAppView): |
@@ -89,6 +89,15 b' class BaseSearcher(object):' | |||||
89 | """ |
|
89 | """ | |
90 | return val |
|
90 | return val | |
91 |
|
91 | |||
|
92 | def sort_def(self, search_type, direction, sort_field): | |||
|
93 | """ | |||
|
94 | Defines sorting for search. This function should decide if for given | |||
|
95 | search_type, sorting can be done with sort_field. | |||
|
96 | ||||
|
97 | It also should translate common sort fields into backend specific. e.g elasticsearch | |||
|
98 | """ | |||
|
99 | raise NotImplementedError() | |||
|
100 | ||||
92 | @staticmethod |
|
101 | @staticmethod | |
93 | def get_sort(search_type, search_val): |
|
102 | def get_sort(search_type, search_val): | |
94 | """ |
|
103 | """ |
@@ -99,6 +99,29 b' class WhooshSearcher(BaseSearcher):' | |||||
99 | query = u'(%s) OR %s' % (query, hashes_or_query) |
|
99 | query = u'(%s) OR %s' % (query, hashes_or_query) | |
100 | return query |
|
100 | return query | |
101 |
|
101 | |||
|
102 | def sort_def(self, search_type, direction, sort_field): | |||
|
103 | ||||
|
104 | if search_type == 'commit': | |||
|
105 | field_defs = { | |||
|
106 | 'message': 'message', | |||
|
107 | 'date': 'date', | |||
|
108 | 'author_email': 'author', | |||
|
109 | } | |||
|
110 | elif search_type == 'path': | |||
|
111 | field_defs = { | |||
|
112 | 'file': 'path', | |||
|
113 | 'size': 'size', | |||
|
114 | 'lines': 'lines', | |||
|
115 | } | |||
|
116 | elif search_type == 'content': | |||
|
117 | # NOTE(dan): content doesn't support any sorting | |||
|
118 | field_defs = {} | |||
|
119 | else: | |||
|
120 | return '' | |||
|
121 | ||||
|
122 | if sort_field in field_defs: | |||
|
123 | return field_defs[sort_field] | |||
|
124 | ||||
102 | def search(self, query, document_type, search_user, |
|
125 | def search(self, query, document_type, search_user, | |
103 | repo_name=None, repo_group_name=None, |
|
126 | repo_name=None, repo_group_name=None, | |
104 | requested_page=1, page_limit=10, sort=None, raise_on_exc=True): |
|
127 | requested_page=1, page_limit=10, sort=None, raise_on_exc=True): | |
@@ -124,21 +147,16 b' class WhooshSearcher(BaseSearcher):' | |||||
124 | query = qp.parse(safe_unicode(query)) |
|
147 | query = qp.parse(safe_unicode(query)) | |
125 | log.debug('query: %s (%s)', query, repr(query)) |
|
148 | log.debug('query: %s (%s)', query, repr(query)) | |
126 |
|
149 | |||
127 | def sort_def(_direction, _sort_field): |
|
|||
128 | field2whoosh = { |
|
|||
129 | 'message.raw': 'message', |
|
|||
130 | 'author.email.raw': 'author', |
|
|||
131 | } |
|
|||
132 | return field2whoosh.get(_sort_field) or _sort_field |
|
|||
133 |
|
||||
134 | reverse, sorted_by = False, None |
|
150 | reverse, sorted_by = False, None | |
135 | direction, sort_field = self.get_sort(search_type, sort) |
|
151 | direction, sort_field = self.get_sort(search_type, sort) | |
136 | if sort_field: |
|
152 | if sort_field: | |
|
153 | sort_definition = self.sort_def(search_type, direction, sort_field) | |||
|
154 | if sort_definition: | |||
|
155 | sorted_by = sort_definition | |||
137 | if direction == Searcher.DIRECTION_DESC: |
|
156 | if direction == Searcher.DIRECTION_DESC: | |
138 | reverse = True |
|
157 | reverse = True | |
139 | if direction == Searcher.DIRECTION_ASC: |
|
158 | if direction == Searcher.DIRECTION_ASC: | |
140 | reverse = False |
|
159 | reverse = False | |
141 | sorted_by = sort_def(direction, sort_field) |
|
|||
142 |
|
160 | |||
143 | whoosh_results = self.searcher.search( |
|
161 | whoosh_results = self.searcher.search( | |
144 | query, filter=allowed_repos_filter, limit=None, |
|
162 | query, filter=allowed_repos_filter, limit=None, |
@@ -69,7 +69,7 b'' | |||||
69 | elif c.sort.startswith('desc:'): |
|
69 | elif c.sort.startswith('desc:'): | |
70 | return c.url_generator(sort='asc:{}'.format(field_name)) |
|
70 | return c.url_generator(sort='asc:{}'.format(field_name)) | |
71 |
|
71 | |||
72 | return 'asc:{}'.format(field_name) |
|
72 | return c.url_generator(sort='asc:{}'.format(field_name)) | |
73 | %> |
|
73 | %> | |
74 | </%def> |
|
74 | </%def> | |
75 |
|
75 |
@@ -9,13 +9,13 b'' | |||||
9 | <th>${_('Commit')}</th> |
|
9 | <th>${_('Commit')}</th> | |
10 | <th></th> |
|
10 | <th></th> | |
11 | <th> |
|
11 | <th> | |
12 |
<a href="${search.field_sort('message |
|
12 | <a href="${search.field_sort('message')}">${_('Commit message')}</a> | |
13 | </th> |
|
13 | </th> | |
14 | <th> |
|
14 | <th> | |
15 |
<a href="${search.field_sort('date')}">${_(' |
|
15 | <a href="${search.field_sort('date')}">${_('Commit date')}</a> | |
16 | </th> |
|
16 | </th> | |
17 | <th> |
|
17 | <th> | |
18 |
<a href="${search.field_sort('author |
|
18 | <a href="${search.field_sort('author_email')}">${_('Author')}</a> | |
19 | </th> |
|
19 | </th> | |
20 | </tr> |
|
20 | </tr> | |
21 | %for entry in c.formatted_results: |
|
21 | %for entry in c.formatted_results: |
@@ -6,7 +6,7 b'' | |||||
6 | <tr> |
|
6 | <tr> | |
7 | <th>${_('Repository')}</th> |
|
7 | <th>${_('Repository')}</th> | |
8 | <th> |
|
8 | <th> | |
9 |
<a href="${search.field_sort('file |
|
9 | <a href="${search.field_sort('file')}">${_('File')}</a> | |
10 | </th> |
|
10 | </th> | |
11 | <th> |
|
11 | <th> | |
12 | <a href="${search.field_sort('size')}">${_('Size')}</a> |
|
12 | <a href="${search.field_sort('size')}">${_('Size')}</a> |
General Comments 0
You need to be logged in to leave comments.
Login now