Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Search faceting #67

Closed
wants to merge 8 commits into from

2 participants

@kura
  • Fixed version of django-haystack
  • Added faceting capabilities (fixing a bug to do so)
  • Added default facet for ItemClass
  • Added default facet for price_range - adding a custom property method to retrieve this
  • Added 2 variables to oscar.defaults for defining price_range and max range in price which is used when price is above any of the ranges defined
  • Default oscar search template now comes with both default facets
  • Added selected_facets hidden CharField to search.forms
@codeinthehole

Killing as it's stale - search is getting a rewrite shortly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Jun 28, 2011
  1. Fixed haystack version

    Kura authored
  2. Fixed haystack version

    Kura authored
  3. Remove reference to author

    Kura authored
  4. Clean up docstrings

    Kura authored
  5. Facets work

    Kura authored
Commits on Jun 29, 2011
  1. Added price range facets

    Kura authored
This page is out of date. Refresh to see the latest.
View
8 oscar/apps/product/abstract_models.py
@@ -9,6 +9,7 @@
from django.template.defaultfilters import slugify
from django.core.urlresolvers import reverse
from django.core.exceptions import ObjectDoesNotExist
+from django.conf import settings
from oscar.apps.product.managers import BrowsableItemManager
@@ -140,6 +141,13 @@ def score(self):
return pr.score
except ObjectDoesNotExist:
return 0
+
+ @property
+ def price_range(self):
+ for price_range in settings.PRICE_RANGES:
+ if self.stockrecord.price_incl_tax < price_range[0]:
+ return price_range[1]
+ return settings.PRICE_RANGE_MAX
def attribute_summary(self):
u"""Return a string of all of a product's attributes"""
View
4 oscar/apps/search/abstract_indexes.py
@@ -1,7 +1,7 @@
from haystack.indexes import *
from oscar.core.loading import import_module
-product_models = import_module('product.models', ['Item'])
+product_models = import_module('product.models', ['Item', 'ItemClass'])
class AbstractProductIndex(SearchIndex):
@@ -12,6 +12,8 @@ class AbstractProductIndex(SearchIndex):
text = EdgeNgramField(document=True, use_template=True, template_name='oscar/search/indexes/product/item_text.txt')
title = EdgeNgramField(model_attr='title')
upc = CharField(model_attr="upc")
+ item_class = CharField(model_attr="item_class", faceted=True)
+ price_range = CharField(model_attr="price_range", faceted=True)
score = FloatField(model_attr="score")
date_created = DateTimeField(model_attr='date_created')
date_updated = DateTimeField(model_attr='date_updated')
View
15 oscar/apps/search/forms.py
@@ -5,25 +5,26 @@
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'
class MultiFacetedSearchForm(FacetedSearchForm):
- '''
+ """
An extension of the regular faceted search form to alow for multiple facets
- '''
+ """
q = forms.CharField(required=False, label=_('Search'), widget=SearchInput({"placeholder": _('Search')}))
-
+ selected_facets = forms.CharField(required=False, widget=forms.HiddenInput())
+
def search(self):
- '''
+ """
Overriding the search method to allow for multiple facets
- '''
+ """
sqs = super(FacetedSearchForm, self).search().order_by('-score')
if hasattr(self, 'cleaned_data') and 'selected_facets' in self.cleaned_data:
for f in self.cleaned_data['selected_facets'].split("|"):
View
2  oscar/apps/search/urls.py
@@ -11,7 +11,7 @@
for field_name, field in ProductIndex.fields.items():
if field.faceted is True:
# Ensure we facet the results set by the defined facetable fields
- sqs.facet(field_name)
+ sqs = sqs.facet(field_name)
urlpatterns = patterns('search.apps.views',
url(r'^suggest/$', Suggestions.as_view(), name='oscar-search-suggest'),
View
14 oscar/apps/search/views.py
@@ -25,9 +25,7 @@ def get(self, request):
return self.render_to_response(context)
def get_context_data(self):
- '''
- Creates a list of suggestions
- '''
+ """Creates a list of suggestions"""
query_term = self.request.GET['query_term'];
query_set = SearchQuerySet().filter(text__contains=query_term)[:self.suggest_limit]
context = []
@@ -39,11 +37,11 @@ def get_context_data(self):
return context
def render_to_response(self, context):
- "Returns a JSON response containing 'context' as payload"
+ """Returns a JSON response containing 'context' as payload"""
return self.get_json_response(self.convert_context_to_json(context))
def get_json_response(self, content, **httpresponse_kwargs):
- "Construct an `HttpResponse` object."
+ """Construct an `HttpResponse` object."""
return HttpResponse(content,
content_type='application/json',
**httpresponse_kwargs)
@@ -80,12 +78,10 @@ def __name__(self):
return "MultiFacetedSearchView"
def extra_context(self):
- '''
- Adds details about the facets applied
- '''
+ """Adds details about the facets applied"""
extra = super(MultiFacetedSearchView, self).extra_context()
- if hasattr(self.form, 'cleaned_data') and 'selected_facets' in self.form.cleaned_data:
+ if hasattr(self.form, 'cleaned_data') and 'selected_facets' in self.form.cleaned_data and self.form.cleaned_data['selected_facets']:
extra['facets_applied'] = []
for f in self.form.cleaned_data['selected_facets'].split("|"):
facet = f.split(":")
View
15 oscar/defaults.py
@@ -25,4 +25,17 @@
(COUNTDOWN, "Countdown"),
(LIST, "List"),
(SINGLE_PRODUCT, "Single product"),
-)
+)
+
+
+
+PRICE_RANGES = (
+ (0, 'FREE'),
+ (10, '0.01-10'),
+ (20, '10-20'),
+ (30, '20-30'),
+ (40, '30-40'),
+ (50, '40-50'),
+)
+
+PRICE_RANGE_MAX = '50+'
View
2  oscar/templates/oscar/search/indexes/product/item_text.txt
@@ -1,2 +1,2 @@
{{ object.title }}
-{{ object.description }}
+{{ object.description }}
View
29 oscar/templates/oscar/search/results.html
@@ -8,6 +8,35 @@
Did you mean <a href="{% url oscar-search %}?q={{ suggestion }}">{{ suggestion }}</a>?
{% endif %}
+{% if query %}
+ <!-- Begin faceting. -->
+ <h2>By type</h2>
+ <div>
+ <ul>
+ {% if facets.fields.item_class %}
+ {% for item_class in facets.fields.item_class|slice:":5" %}
+ <li><a href="{{ request.get_full_path }}&amp;selected_facets=item_class:{{ item_class.0|urlencode }}">{{ item_class.0 }}</a> ({{ item_class.1 }})</li>
+ {% endfor %}
+ {% else %}
+ <p>No type facets.</p>
+ {% endif %}
+ </ul>
+ </div>
+ <h2>Price range</h2>
+ <div>
+ <ul>
+ {% if facets.fields.price_range %}
+ {% for price_range in facets.fields.price_range|slice:":5" %}
+ <li><a href="{{ request.get_full_path }}&amp;selected_facets=price_range:{{ price_range.0|urlencode }}">{{ price_range.0 }}</a> ({{ price_range.1 }})</li>
+ {% endfor %}
+ {% else %}
+ <p>No type facets.</p>
+ {% endif %}
+ </ul>
+ </div>
+ <!-- End faceting -->
+{% endif %}
+
<ol>
{% for result in page.object_list %}
<li>
View
2  requirements-dev.txt
@@ -6,7 +6,7 @@ ipdb==0.2
ipython==0.10.1
wsgiref==0.1.2
flake8==0.8
--e git+https://github.com/toastdriven/django-haystack.git@master#egg=django-haystack
+django-haystack=1.2.4
pysolr==2.0.13
BeautifulSoup==3.2.0
PyZen==0.3.1
View
2  setup.py
@@ -9,5 +9,5 @@
package_dir={'': '.'},
install_requires=['Django>=1.3',
'PIL>=1.1.7',
- 'django-haystack>=1.2.0'],
+ 'django-haystack>=1.2.4'],
)
Something went wrong with that request. Please try again.