##// END OF EJS Templates
search: added per-backend sorting fields....
dan -
r3968:2ec277c2 default
parent child Browse files
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 c.sort = '{}:{}'.format(direction, sort_field)
113 sort_definition = searcher.sort_def(search_type, direction, sort_field)
114 c.sort_tag = sort_field
114 c.sort = ''
115 c.sort_tag = None
115 c.sort_tag_dir = direction
116 c.sort_tag_dir = direction
117 if sort_definition:
118 c.sort = '{}:{}'.format(direction, sort_field)
119 c.sort_tag = sort_field
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:
137 if direction == Searcher.DIRECTION_DESC:
153 sort_definition = self.sort_def(search_type, direction, sort_field)
138 reverse = True
154 if sort_definition:
139 if direction == Searcher.DIRECTION_ASC:
155 sorted_by = sort_definition
140 reverse = False
156 if direction == Searcher.DIRECTION_DESC:
141 sorted_by = sort_def(direction, sort_field)
157 reverse = True
158 if direction == Searcher.DIRECTION_ASC:
159 reverse = False
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.raw')}">${_('Commit message')}</a>
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')}">${_('Age')}</a>
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.email.raw')}">${_('Author')}</a>
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.raw')}">${_('File')}</a>
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