Show More
@@ -1,143 +1,144 | |||||
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 | |||
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 | |||
43 |
|
44 | |||
44 | log = logging.getLogger(__name__) |
|
45 | log = logging.getLogger(__name__) | |
45 |
|
46 | |||
46 |
|
47 | |||
47 | class SearchController(BaseController): |
|
48 | class SearchController(BaseController): | |
48 |
|
49 | |||
49 | @LoginRequired() |
|
50 | @LoginRequired() | |
50 | def __before__(self): |
|
51 | def __before__(self): | |
51 | super(SearchController, self).__before__() |
|
52 | super(SearchController, self).__before__() | |
52 |
|
53 | |||
53 | def index(self, search_repo=None): |
|
54 | def index(self, search_repo=None): | |
54 | c.repo_name = search_repo |
|
55 | c.repo_name = search_repo | |
55 | c.formated_results = [] |
|
56 | c.formated_results = [] | |
56 | c.runtime = '' |
|
57 | c.runtime = '' | |
57 | c.cur_query = request.GET.get('q', None) |
|
58 | c.cur_query = request.GET.get('q', None) | |
58 | c.cur_type = request.GET.get('type', 'content') |
|
59 | c.cur_type = request.GET.get('type', 'content') | |
59 | c.cur_search = search_type = {'content': 'content', |
|
60 | c.cur_search = search_type = {'content': 'content', | |
60 | 'commit': 'message', |
|
61 | 'commit': 'message', | |
61 | 'path': 'path', |
|
62 | 'path': 'path', | |
62 | 'repository': 'repository' |
|
63 | 'repository': 'repository' | |
63 | }.get(c.cur_type, 'content') |
|
64 | }.get(c.cur_type, 'content') | |
64 |
|
65 | |||
65 | index_name = { |
|
66 | index_name = { | |
66 | 'content': IDX_NAME, |
|
67 | 'content': IDX_NAME, | |
67 | 'commit': CHGSET_IDX_NAME, |
|
68 | 'commit': CHGSET_IDX_NAME, | |
68 | 'path': IDX_NAME |
|
69 | 'path': IDX_NAME | |
69 | }.get(c.cur_type, IDX_NAME) |
|
70 | }.get(c.cur_type, IDX_NAME) | |
70 |
|
71 | |||
71 | schema_defn = { |
|
72 | schema_defn = { | |
72 | 'content': SCHEMA, |
|
73 | 'content': SCHEMA, | |
73 | 'commit': CHGSETS_SCHEMA, |
|
74 | 'commit': CHGSETS_SCHEMA, | |
74 | 'path': SCHEMA |
|
75 | 'path': SCHEMA | |
75 | }.get(c.cur_type, SCHEMA) |
|
76 | }.get(c.cur_type, SCHEMA) | |
76 |
|
77 | |||
77 | log.debug('IDX: %s' % index_name) |
|
78 | log.debug('IDX: %s' % index_name) | |
78 | log.debug('SCHEMA: %s' % schema_defn) |
|
79 | log.debug('SCHEMA: %s' % schema_defn) | |
79 |
|
80 | |||
80 | if c.cur_query: |
|
81 | if c.cur_query: | |
81 | cur_query = c.cur_query.lower() |
|
82 | cur_query = c.cur_query.lower() | |
82 | log.debug(cur_query) |
|
83 | log.debug(cur_query) | |
83 |
|
84 | |||
84 | if c.cur_query: |
|
85 | if c.cur_query: | |
85 | p = int(request.params.get('page', 1)) |
|
86 | p = int(request.params.get('page', 1)) | |
86 | highlight_items = set() |
|
87 | highlight_items = set() | |
87 | try: |
|
88 | try: | |
88 | idx = open_dir(config['app_conf']['index_dir'], |
|
89 | idx = open_dir(config['app_conf']['index_dir'], | |
89 | indexname=index_name) |
|
90 | indexname=index_name) | |
90 | searcher = idx.searcher() |
|
91 | searcher = idx.searcher() | |
91 |
|
92 | |||
92 | qp = QueryParser(search_type, schema=schema_defn) |
|
93 | qp = QueryParser(search_type, schema=schema_defn) | |
93 | if c.repo_name: |
|
94 | if c.repo_name: | |
94 | cur_query = u'repository:%s %s' % (c.repo_name, cur_query) |
|
95 | cur_query = u'repository:%s %s' % (c.repo_name, cur_query) | |
95 | try: |
|
96 | try: | |
96 | query = qp.parse(unicode(cur_query)) |
|
97 | query = qp.parse(unicode(cur_query)) | |
97 | # extract words for highlight |
|
98 | # extract words for highlight | |
98 | if isinstance(query, Phrase): |
|
99 | if isinstance(query, Phrase): | |
99 | highlight_items.update(query.words) |
|
100 | highlight_items.update(query.words) | |
100 | elif isinstance(query, Prefix): |
|
101 | elif isinstance(query, Prefix): | |
101 | highlight_items.add(query.text) |
|
102 | highlight_items.add(query.text) | |
102 | else: |
|
103 | else: | |
103 | for i in query.all_terms(): |
|
104 | for i in query.all_terms(): | |
104 | if i[0] in ['content', 'message']: |
|
105 | if i[0] in ['content', 'message']: | |
105 | highlight_items.add(i[1]) |
|
106 | highlight_items.add(i[1]) | |
106 |
|
107 | |||
107 | matcher = query.matcher(searcher) |
|
108 | matcher = query.matcher(searcher) | |
108 |
|
109 | |||
109 | log.debug('query: %s' % query) |
|
110 | log.debug('query: %s' % query) | |
110 | log.debug('hl terms: %s' % highlight_items) |
|
111 | log.debug('hl terms: %s' % highlight_items) | |
111 | results = searcher.search(query) |
|
112 | results = searcher.search(query) | |
112 | res_ln = len(results) |
|
113 | res_ln = len(results) | |
113 | c.runtime = '%s results (%.3f seconds)' % ( |
|
114 | c.runtime = '%s results (%.3f seconds)' % ( | |
114 | res_ln, results.runtime |
|
115 | res_ln, results.runtime | |
115 | ) |
|
116 | ) | |
116 |
|
117 | |||
117 | def url_generator(**kw): |
|
118 | def url_generator(**kw): | |
118 | return update_params("?q=%s&type=%s" \ |
|
119 | return update_params("?q=%s&type=%s" \ | |
119 |
|
|
120 | % (safe_str(c.cur_query), safe_str(c.cur_type)), **kw) | |
120 | repo_location = RepoModel().repos_path |
|
121 | repo_location = RepoModel().repos_path | |
121 | c.formated_results = Page( |
|
122 | c.formated_results = Page( | |
122 | WhooshResultWrapper(search_type, searcher, matcher, |
|
123 | WhooshResultWrapper(search_type, searcher, matcher, | |
123 | highlight_items, repo_location), |
|
124 | highlight_items, repo_location), | |
124 | page=p, |
|
125 | page=p, | |
125 | item_count=res_ln, |
|
126 | item_count=res_ln, | |
126 | items_per_page=10, |
|
127 | items_per_page=10, | |
127 | url=url_generator |
|
128 | url=url_generator | |
128 | ) |
|
129 | ) | |
129 |
|
130 | |||
130 | except QueryParserError: |
|
131 | except QueryParserError: | |
131 | c.runtime = _('Invalid search query. Try quoting it.') |
|
132 | c.runtime = _('Invalid search query. Try quoting it.') | |
132 | searcher.close() |
|
133 | searcher.close() | |
133 | except (EmptyIndexError, IOError): |
|
134 | except (EmptyIndexError, IOError): | |
134 | log.error(traceback.format_exc()) |
|
135 | log.error(traceback.format_exc()) | |
135 | log.error('Empty Index data') |
|
136 | log.error('Empty Index data') | |
136 | c.runtime = _('There is no index to search in. ' |
|
137 | c.runtime = _('There is no index to search in. ' | |
137 | 'Please run whoosh indexer') |
|
138 | 'Please run whoosh indexer') | |
138 | except (Exception): |
|
139 | except (Exception): | |
139 | log.error(traceback.format_exc()) |
|
140 | log.error(traceback.format_exc()) | |
140 | c.runtime = _('An error occurred during this search operation') |
|
141 | c.runtime = _('An error occurred during this search operation') | |
141 |
|
142 | |||
142 | # Return a rendered template |
|
143 | # Return a rendered template | |
143 | return render('/search/search.html') |
|
144 | return render('/search/search.html') |
General Comments 0
You need to be logged in to leave comments.
Login now