Show More
@@ -1,144 +1,146 | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 | """ |
|
2 | """ | |
3 | rhodecode.controllers.search |
|
3 | rhodecode.controllers.search | |
4 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
4 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
5 |
|
5 | |||
6 | Search controller for RhodeCode |
|
6 | Search controller for RhodeCode | |
7 |
|
7 | |||
8 | :created_on: Aug 7, 2010 |
|
8 | :created_on: Aug 7, 2010 | |
9 | :author: marcink |
|
9 | :author: marcink | |
10 | :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com> |
|
10 | :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com> | |
11 | :license: GPLv3, see COPYING for more details. |
|
11 | :license: GPLv3, see COPYING for more details. | |
12 | """ |
|
12 | """ | |
13 | # This program is free software: you can redistribute it and/or modify |
|
13 | # This program is free software: you can redistribute it and/or modify | |
14 | # it under the terms of the GNU General Public License as published by |
|
14 | # it under the terms of the GNU General Public License as published by | |
15 | # the Free Software Foundation, either version 3 of the License, or |
|
15 | # the Free Software Foundation, either version 3 of the License, or | |
16 | # (at your option) any later version. |
|
16 | # (at your option) any later version. | |
17 | # |
|
17 | # | |
18 | # This program is distributed in the hope that it will be useful, |
|
18 | # This program is distributed in the hope that it will be useful, | |
19 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
19 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
20 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
20 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
21 | # GNU General Public License for more details. |
|
21 | # GNU General Public License for more details. | |
22 | # |
|
22 | # | |
23 | # You should have received a copy of the GNU General Public License |
|
23 | # You should have received a copy of the GNU General Public License | |
24 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
24 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
25 | import logging |
|
25 | import logging | |
26 | import traceback |
|
26 | import traceback | |
27 |
|
27 | import urllib | ||
28 | from pylons.i18n.translation import _ |
|
28 | from pylons.i18n.translation import _ | |
29 | from pylons import request, config, tmpl_context as c |
|
29 | from pylons import request, config, tmpl_context as c | |
30 |
|
30 | |||
31 | from rhodecode.lib.auth import LoginRequired |
|
31 | from rhodecode.lib.auth import LoginRequired | |
32 | from rhodecode.lib.base import BaseController, render |
|
32 | from rhodecode.lib.base import BaseController, render | |
33 | from rhodecode.lib.indexers import CHGSETS_SCHEMA, SCHEMA, CHGSET_IDX_NAME, \ |
|
33 | from rhodecode.lib.indexers import CHGSETS_SCHEMA, SCHEMA, CHGSET_IDX_NAME, \ | |
34 | IDX_NAME, WhooshResultWrapper |
|
34 | IDX_NAME, WhooshResultWrapper | |
35 |
|
35 | |||
36 | from webhelpers.paginate import Page |
|
36 | from webhelpers.paginate import Page | |
37 | from webhelpers.util import update_params |
|
37 | from webhelpers.util import update_params | |
38 |
|
38 | |||
39 | from whoosh.index import open_dir, EmptyIndexError |
|
39 | from whoosh.index import open_dir, EmptyIndexError | |
40 | from whoosh.qparser import QueryParser, QueryParserError |
|
40 | from whoosh.qparser import QueryParser, QueryParserError | |
41 | from whoosh.query import Phrase, Wildcard, Term, Prefix |
|
41 | from whoosh.query import Phrase, Wildcard, Term, Prefix | |
42 | from rhodecode.model.repo import RepoModel |
|
42 | from rhodecode.model.repo import RepoModel | |
43 | from rhodecode.lib.utils2 import safe_str, safe_int |
|
43 | from rhodecode.lib.utils2 import safe_str, safe_int | |
44 |
|
44 | |||
|
45 | ||||
45 | log = logging.getLogger(__name__) |
|
46 | log = logging.getLogger(__name__) | |
46 |
|
47 | |||
47 |
|
48 | |||
48 | class SearchController(BaseController): |
|
49 | class SearchController(BaseController): | |
49 |
|
50 | |||
50 | @LoginRequired() |
|
51 | @LoginRequired() | |
51 | def __before__(self): |
|
52 | def __before__(self): | |
52 | super(SearchController, self).__before__() |
|
53 | super(SearchController, self).__before__() | |
53 |
|
54 | |||
54 | def index(self, search_repo=None): |
|
55 | def index(self, search_repo=None): | |
55 | c.repo_name = search_repo |
|
56 | c.repo_name = search_repo | |
56 | c.formated_results = [] |
|
57 | c.formated_results = [] | |
57 | c.runtime = '' |
|
58 | c.runtime = '' | |
58 | c.cur_query = request.GET.get('q', None) |
|
59 | c.cur_query = request.GET.get('q', None) | |
59 | c.cur_type = request.GET.get('type', 'content') |
|
60 | c.cur_type = request.GET.get('type', 'content') | |
60 | c.cur_search = search_type = {'content': 'content', |
|
61 | c.cur_search = search_type = {'content': 'content', | |
61 | 'commit': 'message', |
|
62 | 'commit': 'message', | |
62 | 'path': 'path', |
|
63 | 'path': 'path', | |
63 | 'repository': 'repository' |
|
64 | 'repository': 'repository' | |
64 | }.get(c.cur_type, 'content') |
|
65 | }.get(c.cur_type, 'content') | |
65 |
|
66 | |||
66 | index_name = { |
|
67 | index_name = { | |
67 | 'content': IDX_NAME, |
|
68 | 'content': IDX_NAME, | |
68 | 'commit': CHGSET_IDX_NAME, |
|
69 | 'commit': CHGSET_IDX_NAME, | |
69 | 'path': IDX_NAME |
|
70 | 'path': IDX_NAME | |
70 | }.get(c.cur_type, IDX_NAME) |
|
71 | }.get(c.cur_type, IDX_NAME) | |
71 |
|
72 | |||
72 | schema_defn = { |
|
73 | schema_defn = { | |
73 | 'content': SCHEMA, |
|
74 | 'content': SCHEMA, | |
74 | 'commit': CHGSETS_SCHEMA, |
|
75 | 'commit': CHGSETS_SCHEMA, | |
75 | 'path': SCHEMA |
|
76 | 'path': SCHEMA | |
76 | }.get(c.cur_type, SCHEMA) |
|
77 | }.get(c.cur_type, SCHEMA) | |
77 |
|
78 | |||
78 | log.debug('IDX: %s' % index_name) |
|
79 | log.debug('IDX: %s' % index_name) | |
79 | log.debug('SCHEMA: %s' % schema_defn) |
|
80 | log.debug('SCHEMA: %s' % schema_defn) | |
80 |
|
81 | |||
81 | if c.cur_query: |
|
82 | if c.cur_query: | |
82 | cur_query = c.cur_query.lower() |
|
83 | cur_query = c.cur_query.lower() | |
83 | log.debug(cur_query) |
|
84 | log.debug(cur_query) | |
84 |
|
85 | |||
85 | if c.cur_query: |
|
86 | if c.cur_query: | |
86 | p = safe_int(request.params.get('page', 1), 1) |
|
87 | p = safe_int(request.params.get('page', 1), 1) | |
87 | highlight_items = set() |
|
88 | highlight_items = set() | |
88 | try: |
|
89 | try: | |
89 | idx = open_dir(config['app_conf']['index_dir'], |
|
90 | idx = open_dir(config['app_conf']['index_dir'], | |
90 | indexname=index_name) |
|
91 | indexname=index_name) | |
91 | searcher = idx.searcher() |
|
92 | searcher = idx.searcher() | |
92 |
|
93 | |||
93 | qp = QueryParser(search_type, schema=schema_defn) |
|
94 | qp = QueryParser(search_type, schema=schema_defn) | |
94 | if c.repo_name: |
|
95 | if c.repo_name: | |
95 | cur_query = u'repository:%s %s' % (c.repo_name, cur_query) |
|
96 | cur_query = u'repository:%s %s' % (c.repo_name, cur_query) | |
96 | try: |
|
97 | try: | |
97 | query = qp.parse(unicode(cur_query)) |
|
98 | query = qp.parse(unicode(cur_query)) | |
98 | # extract words for highlight |
|
99 | # extract words for highlight | |
99 | if isinstance(query, Phrase): |
|
100 | if isinstance(query, Phrase): | |
100 | highlight_items.update(query.words) |
|
101 | highlight_items.update(query.words) | |
101 | elif isinstance(query, Prefix): |
|
102 | elif isinstance(query, Prefix): | |
102 | highlight_items.add(query.text) |
|
103 | highlight_items.add(query.text) | |
103 | else: |
|
104 | else: | |
104 | for i in query.all_terms(): |
|
105 | for i in query.all_terms(): | |
105 | if i[0] in ['content', 'message']: |
|
106 | if i[0] in ['content', 'message']: | |
106 | highlight_items.add(i[1]) |
|
107 | highlight_items.add(i[1]) | |
107 |
|
108 | |||
108 | matcher = query.matcher(searcher) |
|
109 | matcher = query.matcher(searcher) | |
109 |
|
110 | |||
110 | log.debug('query: %s' % query) |
|
111 | log.debug('query: %s' % query) | |
111 | log.debug('hl terms: %s' % highlight_items) |
|
112 | log.debug('hl terms: %s' % highlight_items) | |
112 | results = searcher.search(query) |
|
113 | results = searcher.search(query) | |
113 | res_ln = len(results) |
|
114 | res_ln = len(results) | |
114 | c.runtime = '%s results (%.3f seconds)' % ( |
|
115 | c.runtime = '%s results (%.3f seconds)' % ( | |
115 | res_ln, results.runtime |
|
116 | res_ln, results.runtime | |
116 | ) |
|
117 | ) | |
117 |
|
118 | |||
118 | def url_generator(**kw): |
|
119 | def url_generator(**kw): | |
|
120 | q = urllib.quote(safe_str(c.cur_query)) | |||
119 | return update_params("?q=%s&type=%s" \ |
|
121 | return update_params("?q=%s&type=%s" \ | |
120 |
% ( |
|
122 | % (q, safe_str(c.cur_type)), **kw) | |
121 | repo_location = RepoModel().repos_path |
|
123 | repo_location = RepoModel().repos_path | |
122 | c.formated_results = Page( |
|
124 | c.formated_results = Page( | |
123 | WhooshResultWrapper(search_type, searcher, matcher, |
|
125 | WhooshResultWrapper(search_type, searcher, matcher, | |
124 | highlight_items, repo_location), |
|
126 | highlight_items, repo_location), | |
125 | page=p, |
|
127 | page=p, | |
126 | item_count=res_ln, |
|
128 | item_count=res_ln, | |
127 | items_per_page=10, |
|
129 | items_per_page=10, | |
128 | url=url_generator |
|
130 | url=url_generator | |
129 | ) |
|
131 | ) | |
130 |
|
132 | |||
131 | except QueryParserError: |
|
133 | except QueryParserError: | |
132 | c.runtime = _('Invalid search query. Try quoting it.') |
|
134 | c.runtime = _('Invalid search query. Try quoting it.') | |
133 | searcher.close() |
|
135 | searcher.close() | |
134 | except (EmptyIndexError, IOError): |
|
136 | except (EmptyIndexError, IOError): | |
135 | log.error(traceback.format_exc()) |
|
137 | log.error(traceback.format_exc()) | |
136 | log.error('Empty Index data') |
|
138 | log.error('Empty Index data') | |
137 | c.runtime = _('There is no index to search in. ' |
|
139 | c.runtime = _('There is no index to search in. ' | |
138 | 'Please run whoosh indexer') |
|
140 | 'Please run whoosh indexer') | |
139 | except (Exception): |
|
141 | except (Exception): | |
140 | log.error(traceback.format_exc()) |
|
142 | log.error(traceback.format_exc()) | |
141 | c.runtime = _('An error occurred during this search operation') |
|
143 | c.runtime = _('An error occurred during this search operation') | |
142 |
|
144 | |||
143 | # Return a rendered template |
|
145 | # Return a rendered template | |
144 | return render('/search/search.html') |
|
146 | return render('/search/search.html') |
General Comments 0
You need to be logged in to leave comments.
Login now