| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147 |
- from collections import defaultdict
-
- from django import forms
- from django.forms.widgets import Input
- from django.conf import settings
- from django.utils.translation import ugettext_lazy as _
-
- from haystack.forms import FacetedSearchForm
-
- from oscar.core.loading import get_class
-
- is_solr_supported = get_class('search.features', 'is_solr_supported')
-
-
- class SearchInput(Input):
- """
- Defining a search type widget
-
- This is an HTML5 thing and works nicely with Safari, other browsers default
- back to using the default "text" type
- """
- input_type = 'search'
-
-
- # Build a dict of valid queries
- VALID_FACET_QUERIES = defaultdict(list)
- for facet in settings.OSCAR_SEARCH_FACETS['queries'].values():
- field_name = "%s_exact" % facet['field']
- queries = [t[1] for t in facet['queries']]
- VALID_FACET_QUERIES[field_name].extend(queries)
-
-
- class SearchForm(FacetedSearchForm):
- """
- In Haystack, the search form is used for interpreting
- and sub-filtering the SQS.
- """
- # Use a tabindex of 1 so that users can hit tab on any page and it will
- # focus on the search widget.
- q = forms.CharField(
- required=False, label=_('Search'),
- widget=SearchInput({"placeholder": _('Search'), "tabindex": "1"}))
-
- # Search
- RELEVANCY = "relevancy"
- TOP_RATED = "rating"
- NEWEST = "newest"
- PRICE_HIGH_TO_LOW = "price-desc"
- PRICE_LOW_TO_HIGH = "price-asc"
- TITLE_A_TO_Z = "title-asc"
- TITLE_Z_TO_A = "title-desc"
-
- SORT_BY_CHOICES = [
- (RELEVANCY, _("Relevancy")),
- (TOP_RATED, _("Customer rating")),
- (NEWEST, _("Newest")),
- (PRICE_HIGH_TO_LOW, _("Price high to low")),
- (PRICE_LOW_TO_HIGH, _("Price low to high")),
- (TITLE_A_TO_Z, _("Title A to Z")),
- (TITLE_Z_TO_A, _("Title Z to A")),
- ]
-
- # Map query params to sorting fields. Note relevancy isn't included here
- # as we assume results are returned in relevancy order in the absence of an
- # explicit sort field being passed to the search backend.
- SORT_BY_MAP = {
- TOP_RATED: '-rating',
- NEWEST: '-date_created',
- PRICE_HIGH_TO_LOW: '-price',
- PRICE_LOW_TO_HIGH: 'price',
- TITLE_A_TO_Z: 'title_s',
- TITLE_Z_TO_A: '-title_s',
- }
- # Non Solr backends don't support dynamic fields so we just sort on title
- if not is_solr_supported():
- SORT_BY_MAP[TITLE_A_TO_Z] = 'title'
- SORT_BY_MAP[TITLE_Z_TO_A] = '-title'
-
- sort_by = forms.ChoiceField( label=_("Sort by"),
- choices=SORT_BY_CHOICES, widget=forms.Select(), required=False)
-
- @property
- def selected_multi_facets(self):
- """
- Validate and return the selected facets
- """
- # Process selected facets into a dict(field->[*values]) to handle
- # multi-faceting
- selected_multi_facets = defaultdict(list)
-
- for facet_kv in self.selected_facets:
- if ":" not in facet_kv:
- continue
- field_name, value = facet_kv.split(':', 1)
-
- # Validate query facets as they as passed unescaped to Solr
- if field_name in VALID_FACET_QUERIES:
- if value not in VALID_FACET_QUERIES[field_name]:
- # Invalid query value
- continue
-
- selected_multi_facets[field_name].append(value)
-
- return selected_multi_facets
-
- def search(self):
- # We replace the 'search' method from FacetedSearchForm, so that we can
- # handle range queries
- # Note, we call super on a parent class as the default faceted view
- # escapes everything (which doesn't work for price range queries)
- sqs = super(FacetedSearchForm, self).search()
-
- # We need to process each facet to ensure that the field name and the
- # value are quoted correctly and separately:
- for field, values in self.selected_multi_facets.items():
- if not values:
- continue
- if field in VALID_FACET_QUERIES:
- # Query facet - don't wrap value in speech marks and don't
- # clean value. Query values should have been validated by this
- # point and so we don't need to escape them.
- sqs = sqs.narrow(u'%s:(%s)' % (
- field, " OR ".join(values)))
- else:
- # Field facet - clean and quote the values
- clean_values = [
- '"%s"' % sqs.query.clean(val) for val in values]
- sqs = sqs.narrow(u'%s:(%s)' % (
- field, " OR ".join(clean_values)))
-
- if self.is_valid() and 'sort_by' in self.cleaned_data:
- sort_field = self.SORT_BY_MAP.get(
- self.cleaned_data['sort_by'], None)
- if sort_field:
- sqs = sqs.order_by(sort_field)
-
- return sqs
-
-
- class BrowseCategoryForm(SearchForm):
- """
- Variant of SearchForm that returns all products (instead of none) if no
- query is specified.
- """
-
- def no_query_found(self):
- return self.searchqueryset
|