Skip to content
Browse files

More data in the index, added advanced search options & filtering.

  • Loading branch information...
1 parent ed02dff commit 40c747865cbea1e78107f5d3a060809d637a256d @toastdriven toastdriven committed
View
1 AUTHORS
@@ -1 +1,2 @@
* Donald Stufft
+* Daniel Lindsley
View
27 crate_project/apps/packages/search_indexes.py
@@ -1,38 +1,41 @@
from haystack import indexes
-
from packages.models import Package
-# @@@ We should provide some sort of boost from the downloads field
-# (something with an order of magnitude more downloads is probably better)
-# @@@ Should We Filter Out Packages With No Releases?
-
class PackageIndex(indexes.SearchIndex, indexes.Indexable):
text = indexes.CharField(document=True, use_template=True)
name = indexes.CharField(model_attr="name", boost=1.5)
summary = indexes.CharField(null=True)
-
downloads = indexes.IntegerField(model_attr="downloads", indexed=False)
url = indexes.CharField(model_attr="get_absolute_url", indexed=False)
+ platform = indexes.CharField(null=True, faceted=True)
+ license = indexes.CharField(null=True, faceted=True)
+ versions = indexes.MultiValueField(null=True)
+ release_count = indexes.IntegerField(default=0)
+ modified = indexes.DateTimeField(model_attr='modified', faceted=True)
def get_model(self):
return Package
- def prepare_summary(self, obj):
- if obj.latest:
- return obj.latest.summary
-
def prepare(self, obj):
data = super(PackageIndex, self).prepare(obj)
# We want to scale the boost for this document based on how many downloads have
# been recorded for this package.
+ if obj.latest:
+ data['summary'] = obj.latest.summary
+ data['platform'] = obj.latest.platform
+ data['license'] = obj.latest.license
+
+ # Pack in all the versions in decending order.
+ releases = obj.releases.order_by('-created')
+ data['versions'] = [release.version for release in releases if release.version]
+ data['release_count'] = releases.count()
+
# @@@ Might want to actually tier these values instead of percentage them.
# Cap out downloads at 100k
capped_downloads = min(data["downloads"], 10000)
boost = capped_downloads / 10000.0
-
- # @@@ Is 2 too high of a maximum boost?
data["_boost"] = 1.0 + boost
return data
View
16 crate_project/apps/search/forms.py
@@ -1,8 +1,13 @@
+from django import forms
from haystack.forms import SearchForm as HaystackSearchForm
from haystack.inputs import AutoQuery
+from haystack.query import SQ
class SearchForm(HaystackSearchForm):
+ has_releases = forms.BooleanField(required=False, initial=True)
+ start_date = forms.DateField(required=False)
+ end_date = forms.DateField(required=False)
def __init__(self, *args, **kwargs):
super(SearchForm, self).__init__(*args, **kwargs)
@@ -19,7 +24,16 @@ def search(self):
if not self.cleaned_data.get("q"):
return self.no_query_found()
- sqs = self.searchqueryset.filter(content=AutoQuery(self.cleaned_data["q"])).filter_or(name=AutoQuery(self.cleaned_data["q"]))
+ sqs = self.searchqueryset.filter(SQ(content=AutoQuery(self.cleaned_data["q"])) | SQ(name=AutoQuery(self.cleaned_data["q"])))
+
+ if self.cleaned_data.get('has_releases'):
+ sqs = sqs.filter(release_count__gt=0)
+
+ if self.cleaned_data['start_date']:
+ sqs = sqs.filter(modified__gte=self.cleaned_data['start_date'])
+
+ if self.cleaned_data['end_date']:
+ sqs = sqs.filter(modified__lte=self.cleaned_data['end_date'])
if self.load_all:
sqs = sqs.load_all()
View
18 crate_project/apps/search/views.py
@@ -1,3 +1,4 @@
+import datetime
from django.conf import settings
from django.core.paginator import Paginator, InvalidPage
from django.http import Http404
@@ -6,6 +7,7 @@
from django.views.generic.base import TemplateResponseMixin, View
from django.views.generic.edit import FormMixin
+from model_utils.fields import now
from search.forms import SearchForm
@@ -89,12 +91,27 @@ def get_form_kwargs(self):
def form_valid(self, form):
query = form.cleaned_data["q"]
results = form.search()
+ narrow = []
+
+ # Check for facets.
+ if self.request.GET.get('platform'):
+ narrow.append('platform:%s' % self.request.GET.get('platform'))
+
+ if self.request.GET.get('license'):
+ narrow.append('license:%s' % self.request.GET.get('license'))
+
+ if len(narrow):
+ results = results.narrow(' AND '.join(narrow))
page_size = self.get_paginate_by()
if page_size:
+ start_date = form.cleaned_data['start_date'] or datetime.date(1980, 1, 1)
+ end_date = form.cleaned_data['end_date'] or now()
+ facets = results.facet('platform').facet('license').date_facet('modified', start_date, end_date, 'month').facet_counts()
paginator, page, results, is_paginated = self.paginate_results(results, page_size)
else:
+ facets = {}
paginator, page, is_paginated = None, None, False
ctx = {
@@ -104,6 +121,7 @@ def form_valid(self, form):
"page": page,
"paginator": paginator,
"is_paginated": is_paginated,
+ "facets": facets
}
return self.render_to_response(self.get_context_data(**ctx))
View
78 crate_project/templates/search/results.html
@@ -20,20 +20,78 @@
<form method="GET" action="." class="well form-search">
{{ form.non_field_errors }}
- {% for field in form %}
- {{ field }}
- {% endfor %}
+ <div class="field-wrapper">
+ {{ form.q.errors }}
+ <label for="id-search" style="display: none;">Search:</label>
+ {{ form.q }}
+ </div>
+
+ <fieldset>
+ <title>Advanced Options</title>
+
+ <div class="field-wrapper">
+ {{ form.has_releases.errors }}
+ <label for="id-has_releases">Has Releases:</label>
+ {{ form.has_releases }}
+ </div>
+
+ <div class="field-wrapper">
+ {{ form.start_date.errors }}
+ <label for="id-start_date">Start Date:</label>
+ {{ form.start_date }}
+ </div>
+
+ <div class="field-wrapper">
+ {{ form.end_date.errors }}
+ <label for="id-end_date">End Date:</label>
+ {{ form.end_date }}
+ </div>
+ </fieldset>
<input type="submit" class="btn primary" value="Search">
</form>
- {% for result in results %}
- <div class="results">
- {% include "search/partials/package.html" with result=result %}
- </div>
- {% empty %}
- <p>No results found.</p>
- {% endfor %}
+ <div class="facets" style="background-color: #F5F5F5; border: 1px solid rgba(0, 0, 0, 0.05); border-radius: 4px 4px 4px 4px; box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05) inset; float: left; padding: 10px; margin: 0 20px 20px 0; width: 200px;">
+ {% if facets.fields and facets.fields.platform %}
+ <h3>By Platform</h3>
+
+ <ul>
+ {% for plat in facets.fields.platform %}
+ <li><a href="?platform={{ plat.0 }}">{{ plat.0|default:'No platform' }} - {{ plat.1 }}</a></li>
+ {% endfor %}
+ </ul>
+ {% endif %}
+
+ {% if facets.fields and facets.fields.license %}
+ <h3>By License</h3>
+
+ <ul>
+ {% for lice in facets.fields.license %}
+ <li><a href="?license={{ lice.0 }}">{{ lice.0|default:'No license' }} - {{ lice.1 }}</a></li>
+ {% endfor %}
+ </ul>
+ {% endif %}
+
+ {% if facets.dates and facets.dates.modified %}
+ <h3>By Date</h3>
+
+ <ul>
+ {% for mod in facets.dates.modified %}
+ <li><a href="?start_date={{ mod.0 }}">{{ mod.0 }} - {{ mod.1 }}</a></li>
+ {% endfor %}
+ </ul>
+ {% endif %}
+ </div>
+
+ <div class="results-wrapper" style="float: left; width: 690px;">
+ {% for result in results %}
+ <div class="results">
+ {% include "search/partials/package.html" with result=result %}
+ </div>
+ {% empty %}
+ <p>No results found.</p>
+ {% endfor %}
+ </div>
{% if is_paginated %}
<div class="pagination">
View
10 crate_project/templates/search/search.html
@@ -30,9 +30,13 @@
<form method="GET" action="." class="form-search">
{{ form.non_field_errors }}
- {% for field in form %}
- {{ field }}
- {% endfor %}
+ <div class="field-wrapper">
+ {{ form.q.errors }}
+ <label for="id-search" style="display: none;">Search:</label>
+ {{ form.q }}
+ </div>
+
+ <input type="hidden" name="has_releases" value="on">
<input type="submit" class="btn primary" value="Search">
</form>

0 comments on commit 40c7478

Please sign in to comment.
Something went wrong with that request. Please try again.