##// END OF EJS Templates
fixes issues #566 non-ascii search params are crashing paginator...
marcink -
r2842:14852cad beta
parent child Browse files
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 % (c.cur_query, c.cur_type), **kw)
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