Commit 88030a10 by benjaoming

Add Haystack search plugin (NB! Whoosh backend is broken upstream)

parent e21da478
...@@ -4,6 +4,7 @@ dist ...@@ -4,6 +4,7 @@ dist
*.egg-info *.egg-info
README.rst README.rst
testproject/testproject/whoosh_index/ testproject/testproject/whoosh_index/
testproject/testproject/xapian_index/
#Docs #Docs
docs/_build docs/_build
......
...@@ -13,9 +13,22 @@ HAYSTACK_CONNECTIONS = { ...@@ -13,9 +13,22 @@ HAYSTACK_CONNECTIONS = {
'ENGINE': 'haystack.backends.simple_backend.SimpleEngine', 'ENGINE': 'haystack.backends.simple_backend.SimpleEngine',
}, },
} }
HAYSTACK_CONNECTIONS = { HAYSTACK_CONNECTIONS = {
'default': { 'default': {
'ENGINE': 'haystack.backends.whoosh_backend.WhooshEngine', 'ENGINE': 'xapian_backend.XapianEngine',
'PATH': os.path.join(PROJECT_PATH, 'whoosh_index'), 'PATH': os.path.join(PROJECT_PATH, 'xapian_index'),
}, },
} }
# Whoosh backend is completely broken
# https://github.com/toastdriven/django-haystack/issues/522
# https://github.com/toastdriven/django-haystack/issues/382
# https://github.com/toastdriven/django-haystack/issues/447
#HAYSTACK_CONNECTIONS = {
# 'default': {
# 'ENGINE': 'haystack.backends.whoosh_backend.WhooshEngine',
# 'PATH': os.path.join(PROJECT_PATH, 'whoosh_index'),
# },
#}
Haystack plugin
===============
***July 16 2013***
Tested backend, xapian with Haystack 2.0. NB! Get the xapian from Git
https://github.com/notanumber/xapian-haystack
Not working
-----------
Do not use Whoosh, it has broken SearchQuerySet support and therefore will leak
articles that are set non-public.
from wiki.core import settings
from wiki.core import permissions
from wiki import models
from haystack import views as haystack_views
from django.db.models import Q
from django.utils.decorators import classonlymethod
from django.shortcuts import redirect
class SearchViewHaystack(haystack_views.SearchView):
results_per_page = 25
template = "search/search.html"
def __name__(self): #@ReservedAssignment
return "SearchViewHaystack"
@classonlymethod
def as_view(cls,*args,**kwargs):
return haystack_views.search_view_factory(view_class=cls,*args,**kwargs)
def dispatch(self, request, *args, **kwargs):
# Do not allow anonymous users to search if they cannot read content
if request.user.is_anonymous() and not settings.ANONYMOUS:
return redirect(settings.LOGIN_URL)
return super(SearchViewHaystack, self).dispatch(request, *args, **kwargs)
def __filter_can_read(self, user):
"""Filter objects so only the ones with a user's reading access
are included"""
if user.has_perm('wiki.moderator'):
return self.results
if user.is_anonymous():
q = self.results.filter(other_read='True')
return q
else:
q = self.results.filter(
Q(other_read=True) |
Q(owner=user) |
(Q(group__user=user) & Q(group_read=True))
)
return q
def __call__(self, request):
self.request = request
if self.request.user.is_anonymous and not settings.ANONYMOUS:
return redirect(settings.LOGIN_URL)
self.form = self.build_form()
self.query = self.get_query()
self.results = self.get_results()
if not permissions.can_moderate(models.URLPath.root().article, self.request.user):
self.results = self.__filter_can_read(self.request.user)
#self.results = self.results.filter(current_revision__deleted=False)
return self.create_response()
def extra_context(self):
extra = super(SearchViewHaystack, self).extra_context()
extra['search_query'] = self.query
return extra
from haystack import indexes
from wiki import models
class ArticleIndex(indexes.SearchIndex, indexes.Indexable):
text = indexes.CharField(document=True, use_template=True)
created = indexes.DateTimeField(model_attr='created')
modified = indexes.DateTimeField(model_attr='modified')
#default because indexing fails with whoosh. see.
#http://stackoverflow.com/questions/11995367/how-do-i-use-a-boolean-field-in-django-haystack-search-query
#https://github.com/toastdriven/django-haystack/issues/382
other_read = indexes.BooleanField(model_attr='other_read',default=False)
group_read = indexes.BooleanField(model_attr='group_read',default=False)
owner_id = indexes.IntegerField(model_attr='owner__id')
group_id = indexes.IntegerField(model_attr='group__id')
def get_model(self):
return models.Article
def index_queryset(self, using=None):
"""Used when the entire index for model is updated."""
return self.get_model().objects.all()
{{ object.current_revision.title }}
{{ object.current_revision.content }}
{% extends "wiki/search.html" %}
{% block wiki_search_loop %}
{% with article.object as article %}
{% include "wiki/includes/searchresult.html" %}
{% endwith %}
{% endblock %}
from wiki.views.article import SearchView
from haystack.query import SearchQuerySet
from haystack.inputs import AutoQuery
from wiki.core import permissions
from wiki import models
class HaystackSearchView(SearchView):
template_name = 'wiki/plugins/haystack/search.html'
def get_queryset(self):
qs = SearchQuerySet().all()
if self.request.user.is_authenticated():
# TODO: This has a leak! It should say:
# group=self.request.group.id AND group_read=True
if not permissions.can_moderate(models.URLPath.root().article,
self.request.user):
qs = qs.filter_or(
owner=self.request.user.id,
group=self.request.group.id,
other_read=True
)
else:
qs = qs.exclude(other_read=False)
qs = qs.filter(content=AutoQuery(self.query))
qs = qs.exclude(other_read=False)
qs = qs.load_all()
return qs
\ No newline at end of file
{% load wiki_tags i18n humanize %}
{% load url from future %}
<tr>
<td>
{% for urlpath in article.urlpath_set.all %}
<a href="{% url 'wiki:get' path=urlpath.path %}">{{ article.current_revision.title }}<br /><small class="muted">/{{ urlpath.path }}</small></a>
{% empty %}
<a href="{% url 'wiki:get' article_id=article.id %}">{{ article.current_revision.title }}</a>
{% endfor %}
{% if article.current_revision.deleted %}
<span class="icon-trash"></span>
{% endif %}
{% if article.current_revision.locked %}
<span class="icon-lock"></span>
{% endif %}
<p class="muted"><small>{{ article.current_revision.content|get_content_snippet:search_query }}</small></p>
</td>
<td style="white-space: nowrap">
{{ article.current_revision.created|naturaltime }}
</td>
</tr>
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
<form class="form-search directory-toolbar"> <form class="form-search directory-toolbar">
<p class="lead"> <p class="lead">
<div class="pull-right"> <div class="pull-right">
{{ search_form.query }} {{ search_form.q }}
<button class="btn"><span class="icon-search"></span></button> <button class="btn"><span class="icon-search"></span></button>
</div> </div>
<p>{% blocktrans with paginator.object_list.count as cnt %}Your search returned <strong>{{ cnt }}</strong> results.{% endblocktrans %}</p> <p>{% blocktrans with paginator.object_list.count as cnt %}Your search returned <strong>{{ cnt }}</strong> results.{% endblocktrans %}</p>
...@@ -25,25 +25,9 @@ ...@@ -25,25 +25,9 @@
<th>{% trans "Last modified" %}</th> <th>{% trans "Last modified" %}</th>
</tr> </tr>
{% for article in articles %} {% for article in articles %}
<tr> {% block wiki_search_loop %}
<td> {% include "wiki/includes/searchresult.html" %}
{% for urlpath in article.urlpath_set.all %} {% endblock %}
<a href="{% url 'wiki:get' path=urlpath.path %}">{{ article.current_revision.title }}<br /><small class="muted">/{{ urlpath.path }}</small></a>
{% empty %}
<a href="{% url 'wiki:get' article_id=article.id %}">{{ article.current_revision.title }}</a>
{% endfor %}
{% if article.current_revision.deleted %}
<span class="icon-trash"></span>
{% endif %}
{% if article.current_revision.locked %}
<span class="icon-lock"></span>
{% endif %}
<p class="muted"><small>{{ article.current_revision.content|get_content_snippet:search_query }}</small></p>
</td>
<td style="white-space: nowrap">
{{ article.current_revision.created|naturaltime }}
</td>
</tr>
{% empty%} {% empty%}
<tr> <tr>
<td colspan="100"> <td colspan="100">
......
...@@ -11,7 +11,6 @@ from django.utils.translation import ugettext as _ ...@@ -11,7 +11,6 @@ from django.utils.translation import ugettext as _
from django.views.generic.base import TemplateView, View from django.views.generic.base import TemplateView, View
from django.views.generic.edit import FormView from django.views.generic.edit import FormView
from django.views.generic.list import ListView from django.views.generic.list import ListView
from django.utils.decorators import classonlymethod
from wiki.views.mixins import ArticleMixin from wiki.views.mixins import ArticleMixin
from wiki import editors, forms, models from wiki import editors, forms, models
...@@ -24,7 +23,7 @@ from django.db import transaction ...@@ -24,7 +23,7 @@ from django.db import transaction
from wiki.core.exceptions import NoRootURL from wiki.core.exceptions import NoRootURL
from wiki.core import permissions from wiki.core import permissions
from django.http import Http404 from django.http import Http404
from haystack import views as haystack_views
class ArticleView(ArticleMixin, TemplateView): class ArticleView(ArticleMixin, TemplateView):
...@@ -465,58 +464,6 @@ class Dir(ListView, ArticleMixin): ...@@ -465,58 +464,6 @@ class Dir(ListView, ArticleMixin):
return kwargs return kwargs
class SearchViewHaystack(haystack_views.SearchView):
results_per_page = 25
template = "search/search.html"
def __name__(self):
return "SearchViewHaystack"
def dispatch(self, request, *args, **kwargs):
# Do not allow anonymous users to search if they cannot read content
if request.user.is_anonymous() and not settings.ANONYMOUS:
return redirect(settings.LOGIN_URL)
return super(SearchViewHaystack, self).dispatch(request, *args, **kwargs)
@classonlymethod
def as_view(cls,*args,**kwargs):
return haystack_views.search_view_factory(view_class=cls,*args,**kwargs)
def __filter_can_read(self, user):
"""Filter objects so only the ones with a user's reading access
are included"""
if user.has_perm('wiki.moderator'):
return self.results
if user.is_anonymous():
q = self.results.filter(other_read='True')
return q
else:
q = self.results.filter(Q(other_read=True) |
Q(owner=user) |
(Q(group__user=user) & Q(group_read=True))
)
return q
def __call__(self, request):
self.request = request
if self.request.user.is_anonymous and not settings.ANONYMOUS:
return redirect(settings.LOGIN_URL)
self.form = self.build_form()
self.query = self.get_query()
self.results = self.get_results()
if not permissions.can_moderate(models.URLPath.root().article, self.request.user):
self.results = self.__filter_can_read(self.request.user)
#self.results = self.results.filter(current_revision__deleted=False)
return self.create_response()
def extra_context(self):
extra = super(SearchViewHaystack, self).extra_context()
extra['search_query'] = self.query
return extra
class SearchView(ListView): class SearchView(ListView):
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment