Commit 88030a10 by benjaoming

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

parent e21da478
......@@ -4,6 +4,7 @@ dist
*.egg-info
README.rst
testproject/testproject/whoosh_index/
testproject/testproject/xapian_index/
#Docs
docs/_build
......
......@@ -13,9 +13,22 @@ HAYSTACK_CONNECTIONS = {
'ENGINE': 'haystack.backends.simple_backend.SimpleEngine',
},
}
HAYSTACK_CONNECTIONS = {
'default': {
'ENGINE': 'haystack.backends.whoosh_backend.WhooshEngine',
'PATH': os.path.join(PROJECT_PATH, 'whoosh_index'),
'ENGINE': 'xapian_backend.XapianEngine',
'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 @@
<form class="form-search directory-toolbar">
<p class="lead">
<div class="pull-right">
{{ search_form.query }}
{{ search_form.q }}
<button class="btn"><span class="icon-search"></span></button>
</div>
<p>{% blocktrans with paginator.object_list.count as cnt %}Your search returned <strong>{{ cnt }}</strong> results.{% endblocktrans %}</p>
......@@ -25,25 +25,9 @@
<th>{% trans "Last modified" %}</th>
</tr>
{% for article in articles %}
<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>
{% block wiki_search_loop %}
{% include "wiki/includes/searchresult.html" %}
{% endblock %}
{% empty%}
<tr>
<td colspan="100">
......
......@@ -11,7 +11,6 @@ from django.utils.translation import ugettext as _
from django.views.generic.base import TemplateView, View
from django.views.generic.edit import FormView
from django.views.generic.list import ListView
from django.utils.decorators import classonlymethod
from wiki.views.mixins import ArticleMixin
from wiki import editors, forms, models
......@@ -24,7 +23,7 @@ from django.db import transaction
from wiki.core.exceptions import NoRootURL
from wiki.core import permissions
from django.http import Http404
from haystack import views as haystack_views
class ArticleView(ArticleMixin, TemplateView):
......@@ -465,58 +464,6 @@ class Dir(ListView, ArticleMixin):
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):
......
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