1 import haystack.backends.solr_backend
2 from haystack.query import SearchQuerySet, RelatedSearchQuerySet
3 from haystack.constants import ID, DJANGO_CT, DJANGO_ID
4 from haystack.utils import get_identifier
5 from haystack.backends import EmptyResults
7 from pysolr import SolrError
9 from django import forms
10 from django.conf import settings
11 from django.utils.translation import ugettext_lazy as _
13 from emissions.models import Emission, Episode, NewsItem, SoundFile
16 class MoreLikeThisSearchQuerySet(SearchQuerySet):
17 def more_like_this(self, model_instance, **kwargs):
19 clone.query.more_like_this(model_instance, **kwargs)
23 class CustomSolrSearchQuery(haystack.backends.solr_backend.SolrSearchQuery):
24 def more_like_this(self, model_instance, **kwargs):
25 self._more_like_this = True
26 self._mlt_instance = model_instance
28 def run_mlt(self, **kwargs):
29 """Builds and executes the query. Returns a list of search results."""
30 if self._more_like_this is False or self._mlt_instance is None:
31 raise MoreLikeThisError("No instance was provided to determine 'More Like This' results.")
33 additional_query_string = self.build_query()
35 'start_offset': self.start_offset,
36 'result_class': self.result_class,
37 'models': self.models,
41 if self.end_offset is not None:
42 search_kwargs['end_offset'] = self.end_offset - self.start_offset
44 results = self.backend.more_like_this(self._mlt_instance, additional_query_string, **search_kwargs)
45 self._results = results.get('results', [])
46 self._hit_count = results.get('hits', 0)
49 class CustomSolrSearchBackend(haystack.backends.solr_backend.SolrSearchBackend):
50 def more_like_this(self, model_instance, additional_query_string=None,
51 start_offset=0, end_offset=None, models=None,
52 limit_to_registered_models=None, result_class=None, **kwargs):
53 from haystack import connections
55 # Deferred models will have a different class ("RealClass_Deferred_fieldname")
56 # which won't be in our registry:
57 model_klass = model_instance._meta.concrete_model
59 index = connections[self.connection_alias].get_unified_index().get_index(model_klass)
60 field_name = index.get_content_field()
63 'mlt.fl': 'text,title',
64 'mlt.qf': 'text^0.3 tags^2.0 title^1.0',
67 if start_offset is not None:
68 params['start'] = start_offset
70 if end_offset is not None:
71 params['rows'] = end_offset
73 narrow_queries = set()
75 if limit_to_registered_models is None:
76 limit_to_registered_models = getattr(settings, 'HAYSTACK_LIMIT_TO_REGISTERED_MODELS', True)
78 if models and len(models):
79 model_choices = sorted(['%s.%s' % (model._meta.app_label, model._meta.module_name) for model in models])
80 elif limit_to_registered_models:
81 # Using narrow queries, limit the results to only models handled
82 # with the current routers.
83 model_choices = self.build_models_list()
87 if len(model_choices) > 0:
88 if narrow_queries is None:
89 narrow_queries = set()
91 narrow_queries.add('%s:(%s)' % (DJANGO_CT, ' OR '.join(model_choices)))
93 if additional_query_string:
94 narrow_queries.add(additional_query_string)
97 params['fq'] = list(narrow_queries)
99 query = "%s:%s" % (ID, get_identifier(model_instance))
102 raw_results = self.conn.more_like_this(query, field_name, **params)
103 except (IOError, SolrError) as e:
104 if not self.silently_fail:
107 self.log.error("Failed to fetch More Like This from Solr for document '%s': %s", query, e)
108 raw_results = EmptyResults()
110 return self._process_results(raw_results, result_class=result_class)
113 haystack.backends.solr_backend.SolrEngine.query = CustomSolrSearchQuery
114 haystack.backends.solr_backend.SolrEngine.backend = CustomSolrSearchBackend
116 import haystack.views
117 from haystack.views import search_view_factory, FacetedSearchView
118 from haystack.forms import FacetedSearchForm, SearchForm
121 class GlobalSearchForm(FacetedSearchForm):
122 def no_query_found(self):
123 sqs = super(GlobalSearchForm, self).no_query_found()
124 if self.selected_facets:
125 sqs = self.searchqueryset.all()
126 for facet in self.selected_facets:
129 field, value = facet.split(":", 1)
131 sqs = sqs.narrow(u'%s:"%s"' % (field, sqs.query.clean(value)))
135 class SearchView(FacetedSearchView):
136 def extra_context(self):
137 context = super(SearchView, self).extra_context()
138 context['sectionName'] = 'Search'
139 context['selected_categories'] = [
140 x.split(':', 1)[1] for x in self.request.GET.getlist('selected_facets')
141 if x.startswith('categories_exact')]
142 context['selected_tags'] = [
143 x.split(':', 1)[1] for x in self.request.GET.getlist('selected_facets')
144 if x.startswith('tags_exact')]
147 sqs = SearchQuerySet().models(Emission, Episode, NewsItem).facet('categories').facet('tags')
149 view = search_view_factory(SearchView,
150 form_class=GlobalSearchForm,
155 class ListenArchivesForm(FacetedSearchForm):
156 q = forms.CharField(required=False, label=_('Search Sounds'))
158 def no_query_found(self):
159 return self.searchqueryset.all()
162 sqs = super(ListenArchivesForm, self).search()
163 return sqs.load_all()
166 class ListenArchivesView(FacetedSearchView):
167 template = 'listen/archives.html'
170 sqs = RelatedSearchQuerySet().models(SoundFile).facet('categories').facet('tags').order_by('-date')
171 super(ListenArchivesView, self).__init__(searchqueryset=sqs,
172 form_class=ListenArchivesForm, results_per_page=20)
174 def extra_context(self):
175 context = super(ListenArchivesView, self).extra_context()
176 context['sectionName'] = "Listen"
177 context['selected_categories'] = [
178 x.split(':', 1)[1] for x in self.request.GET.getlist('selected_facets')
179 if x.startswith('categories_exact')]
180 context['selected_tags'] = [
181 x.split(':', 1)[1] for x in self.request.GET.getlist('selected_facets')
182 if x.startswith('tags_exact')]
183 context['facets']['fields']['categories'].sort()
186 listenArchives = search_view_factory(ListenArchivesView)
189 class NewsArchivesForm(FacetedSearchForm):
190 q = forms.CharField(required=False, label=_('Search News'))
192 def no_query_found(self):
193 return self.searchqueryset.all()
196 sqs = super(NewsArchivesForm, self).search()
197 return sqs.load_all()
200 class NewsArchivesView(FacetedSearchView):
201 template = 'news/archives.html'
204 sqs = RelatedSearchQuerySet().models(NewsItem).facet('news_categories').facet('tags').order_by('-date')
205 super(NewsArchivesView, self).__init__(searchqueryset=sqs,
206 form_class=NewsArchivesForm, results_per_page=20)
208 def extra_context(self):
209 context = super(NewsArchivesView, self).extra_context()
210 context['sectionName'] = 'News'
211 context['selected_news_categories'] = [
212 x.split(':', 1)[1] for x in self.request.GET.getlist('selected_facets')
213 if x.startswith('news_categories_exact')]
214 context['selected_tags'] = [
215 x.split(':', 1)[1] for x in self.request.GET.getlist('selected_facets')
216 if x.startswith('tags_exact')]
219 newsArchives = search_view_factory(NewsArchivesView)