]> git.0d.be Git - panikweb.git/blob - panikweb/search.py
use haystack to fill a 'related' section down in the page
[panikweb.git] / panikweb / search.py
1 import haystack.backends.solr_backend
2 from haystack.query import SearchQuerySet
3 from haystack.constants import ID, DJANGO_CT, DJANGO_ID
4 from haystack.utils import get_identifier
5
6
7 from django.conf import settings
8
9
10 class MoreLikeThisSearchQuerySet(SearchQuerySet):
11     def more_like_this(self, model_instance, **kwargs):
12         clone = self._clone()
13         clone.query.more_like_this(model_instance, **kwargs)
14         return clone
15
16
17 class CustomSolrSearchQuery(haystack.backends.solr_backend.SolrSearchQuery):
18     def more_like_this(self, model_instance, **kwargs):
19         self._more_like_this = True
20         self._mlt_instance = model_instance
21
22     def run_mlt(self, **kwargs):
23         """Builds and executes the query. Returns a list of search results."""
24         if self._more_like_this is False or self._mlt_instance is None:
25             raise MoreLikeThisError("No instance was provided to determine 'More Like This' results.")
26
27         additional_query_string = self.build_query()
28         search_kwargs = {
29             'start_offset': self.start_offset,
30             'result_class': self.result_class,
31             'models': self.models,
32             'mlt.fl': 'tags',
33         }
34
35         if self.end_offset is not None:
36             search_kwargs['end_offset'] = self.end_offset - self.start_offset
37
38         results = self.backend.more_like_this(self._mlt_instance, additional_query_string, **search_kwargs)
39         self._results = results.get('results', [])
40         self._hit_count = results.get('hits', 0)
41
42
43 class CustomSolrSearchBackend(haystack.backends.solr_backend.SolrSearchBackend):
44     def more_like_this(self, model_instance, additional_query_string=None,
45                        start_offset=0, end_offset=None, models=None,
46                        limit_to_registered_models=None, result_class=None, **kwargs):
47         from haystack import connections
48
49         # Deferred models will have a different class ("RealClass_Deferred_fieldname")
50         # which won't be in our registry:
51         model_klass = model_instance._meta.concrete_model
52
53         index = connections[self.connection_alias].get_unified_index().get_index(model_klass)
54         field_name = index.get_content_field()
55         params = {
56             'fl': '*,score',
57             'mlt.fl': 'text,title',
58             'mlt.qf': 'text^0.3 tags^2.0 title^1.0',
59         }
60
61         if start_offset is not None:
62             params['start'] = start_offset
63
64         if end_offset is not None:
65             params['rows'] = end_offset
66
67         narrow_queries = set()
68
69         if limit_to_registered_models is None:
70             limit_to_registered_models = getattr(settings, 'HAYSTACK_LIMIT_TO_REGISTERED_MODELS', True)
71
72         if models and len(models):
73             model_choices = sorted(['%s.%s' % (model._meta.app_label, model._meta.module_name) for model in models])
74         elif limit_to_registered_models:
75             # Using narrow queries, limit the results to only models handled
76             # with the current routers.
77             model_choices = self.build_models_list()
78         else:
79             model_choices = []
80
81         if len(model_choices) > 0:
82             if narrow_queries is None:
83                 narrow_queries = set()
84
85             narrow_queries.add('%s:(%s)' % (DJANGO_CT, ' OR '.join(model_choices)))
86
87         if additional_query_string:
88             narrow_queries.add(additional_query_string)
89
90         if narrow_queries:
91             params['fq'] = list(narrow_queries)
92
93         query = "%s:%s" % (ID, get_identifier(model_instance))
94
95         try:
96             raw_results = self.conn.more_like_this(query, field_name, **params)
97         except (IOError, SolrError) as e:
98             if not self.silently_fail:
99                 raise
100
101             self.log.error("Failed to fetch More Like This from Solr for document '%s': %s", query, e)
102             raw_results = EmptyResults()
103
104         return self._process_results(raw_results, result_class=result_class)
105
106
107 haystack.backends.solr_backend.SolrEngine.query = CustomSolrSearchQuery
108 haystack.backends.solr_backend.SolrEngine.backend = CustomSolrSearchBackend
109
110 from haystack.views import search_view_factory, FacetedSearchView
111 from haystack.forms import FacetedSearchForm
112
113
114 class SearchView(FacetedSearchView):
115     def extra_context(self):
116         context = super(SearchView, self).extra_context()
117         context['sectionName'] = 'Search'
118         context['selected_categories'] = [
119                 x.split(':', 1)[1] for x in self.request.GET.getlist('selected_facets')
120                 if x.startswith('categories_exact')]
121         context['selected_tags'] = [
122                 x.split(':', 1)[1] for x in self.request.GET.getlist('selected_facets')
123                 if x.startswith('tags_exact')]
124         return context
125
126 sqs = SearchQuerySet().facet('categories').facet('tags')
127
128 view = search_view_factory(SearchView,
129         form_class=FacetedSearchForm,
130         searchqueryset=sqs)