Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Extend Index.index_queryset / read_queryset to see the active backend #534

Closed
wants to merge 9 commits into from

4 participants

@acdha
Owner

This allows the index to customize the queryset based on the active backend. This is extremely handy for multi-lingual work as it means the index_queryset can do things like exclude documents which aren't in the correct language, as demonstrated in the attached documentation update.

@mvdwaeter

Im using his fork right now. It is exactly what Im looking for to use multi-language indexation.

@acdha
Owner

I've rebased my branch against the current master

@acdha
Owner

This leaves one design change: currently update_index / clear_index / rebuild_index use an assumed default via --using. With this change, it would become more useful to have something like a --using=ALL or changing the implied default so the lack of --using means that all registered backends will be updated.

@toastdriven
Owner

@acdha I'd introduce a new constant for all backends & use that. If you can get tests passing, I'd say merge away.

@acdha
Owner

Cool - I'll start working on adding a new constant and getting some proper test coverage

@acdha
Owner

As of now, the tests run. The multipleindex tests were failing due to the way the Whoosh index wasn't properly cleaned up.

I still need to write some tests for the coverage path using the provided using value

@acdha
Owner

@toastdriven: I still need the corresponding update to clear_index but this works well in my testing and makes it trivial to update only a selected set of backends

@acdha
Owner

I've added tests to confirm that the management commands behave as expected. I believe this branch is now ready to merge.

@acdha
Owner

rebased against master. @toastdriven - do you want to review this? The tests pass for me locally against Solr 3.6, 4.0, ElasticSearch 0.20.1 (modulo expected failures) and Whoosh

acdha added some commits
@acdha acdha Index index_queryset method can see the target backend
This allows backend-specific logic - for example, with language-specific
backends an index can do something like

    def index_queryset(self, using):
        return qs.filter(language=using)
6c4ccb0
@acdha acdha Make using optional for both index_queryset and read_queryset e3b35fd
@acdha
Owner

update_index.py has a few unintentional merge regressions - mostly minor (comments, etc) but I need to fix them

@toastdriven
Owner

Sorry it took so long to get back here. Looks super-solid. Once the regressions are fixed, no further review needed, :shipit:

acdha added some commits
@acdha acdha Added multi-index examples for backend-based content filtering
This demonstrates how to filter index update / read querysets when
necessary and two examples for how to perform this filtering
77519e8
@acdha acdha Index.update: pass using through to index_queryset() call a0a6254
@acdha acdha Basic tests for filtering index_querysets based on using value 0ede9ad
@acdha acdha PEP-8 4d199e0
@acdha acdha update_index: update all backends by default
* Change the default behaviour to update every backend
* Allow --using to be repeated to update multiple named backends
a018d7b
@acdha acdha clear_index: adopt new behaviour of update_index
* By default, all backends are cleared
* --using may be specified multiple times
1bfb090
@acdha acdha Basic tests for management commands with --using
This ensures that the management commands correctly process --using options and
pass them to the appropriate lower-level code
77146e6
@acdha
Owner

I was too quick to assume bugs: what looked like a regression at first glance was just my misreading a diff I hadn't looked at in awhile. Merge imminent…

@acdha acdha closed this pull request from a commit
@acdha acdha Pass active backend to index queryset calls (closes #534)
Now the Index index_queryset() and read_queryset() methods will be called with
the active backend name so they can optionally perform backend-specific
filtering.

This is extremely useful when using something like Solr cores to maintain
language specific backends, allowing an Index to select the appropriate
documents for each language::

    def index_queryset(self, using=None):
        return Post.objects.filter(language=using)

Changes:
    * clear_index, update_index and rebuild_index all default to processing
      *every* backend. ``--using`` may now be provided multiple times to select
      a subset of the configured backends.
    * Added examples to the Multiple Index documentation page
c5e0ce5
@acdha acdha closed this in c5e0ce5
@acdha acdha referenced this pull request from a commit
Commit has since been removed from the repository and is no longer available.
@floppya floppya referenced this pull request from a commit in floppya/django-haystack
@acdha acdha Pass active backend to index queryset calls (closes #534)
Now the Index index_queryset() and read_queryset() methods will be called with
the active backend name so they can optionally perform backend-specific
filtering.

This is extremely useful when using something like Solr cores to maintain
language specific backends, allowing an Index to select the appropriate
documents for each language::

    def index_queryset(self, using=None):
        return Post.objects.filter(language=using)

Changes:
    * clear_index, update_index and rebuild_index all default to processing
      *every* backend. ``--using`` may now be provided multiple times to select
      a subset of the configured backends.
    * Added examples to the Multiple Index documentation page
779634c
@ntucker

Is it just me or does this break everything?

TypeError
index_queryset() got an unexpected keyword argument 'using'

@ntucker ntucker referenced this pull request from a commit in ntucker/django-haystack
@ntucker ntucker Revert "Pass active backend to index queryset calls (closes #534)"
This reverts commit c5e0ce5.
0228996
@acdha
Owner
@ntucker

Thanks acdha, somehow I missed that in upgrade documentation. :)

@acdha acdha deleted the branch
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Jan 23, 2013
  1. @acdha

    Index index_queryset method can see the target backend

    acdha authored
    This allows backend-specific logic - for example, with language-specific
    backends an index can do something like
    
        def index_queryset(self, using):
            return qs.filter(language=using)
  2. @acdha
Commits on Jan 24, 2013
  1. @acdha

    Added multi-index examples for backend-based content filtering

    acdha authored
    This demonstrates how to filter index update / read querysets when
    necessary and two examples for how to perform this filtering
  2. @acdha
  3. @acdha
  4. @acdha

    PEP-8

    acdha authored
  5. @acdha

    update_index: update all backends by default

    acdha authored
    * Change the default behaviour to update every backend
    * Allow --using to be repeated to update multiple named backends
  6. @acdha

    clear_index: adopt new behaviour of update_index

    acdha authored
    * By default, all backends are cleared
    * --using may be specified multiple times
  7. @acdha

    Basic tests for management commands with --using

    acdha authored
    This ensures that the management commands correctly process --using options and
    pass them to the appropriate lower-level code
This page is out of date. Refresh to see the latest.
View
2  docs/autocomplete.rst
@@ -42,7 +42,7 @@ Example (continuing from the tutorial)::
def get_model(self):
return Note
- def index_queryset(self):
+ def index_queryset(self, using=None):
"""Used when the entire index for model is updated."""
return Note.objects.filter(pub_date__lte=datetime.datetime.now())
View
2  docs/migration_from_1_to_2.rst
@@ -155,7 +155,7 @@ A converted Haystack 2.X index should look like::
def get_model(self):
return Note
- def index_queryset(self):
+ def index_queryset(self, using=None):
"""Used when the entire index for model is updated."""
return self.get_model().objects.filter(pub_date__lte=datetime.datetime.now())
View
36 docs/multiple_index.rst
@@ -163,3 +163,39 @@ via the ``SearchQuerySet.using`` method::
Note that the models a ``SearchQuerySet`` is trying to pull from must all come
from the same index. Haystack is not able to combine search queries against
different indexes.
+
+
+Custom Index Selection
+======================
+
+If a specific backend has been selected, the ``SearchIndex.index_queryset`` and
+``SearchIndex.read_queryset`` will receive the backend name, giving indexes the
+opportunity to customize the returned queryset.
+
+For example, a site which uses separate indexes for recent items and older
+content might define ``index_queryset`` to filter the items based on date::
+
+ def index_queryset(self, using=None):
+ qs = Note.objects.all()
+ archive_limit = datetime.datetime.now() - datetime.timedelta(days=90)
+
+ if using == "archive":
+ return qs.filter(pub_date__lte=archive_limit)
+ else:
+ return qs.filter(pub_date__gte=archive_limit)
+
+
+Multi-lingual Content
+---------------------
+
+Most search engines require you to set the language at the index level. For
+example, a multi-lingual site using Solr can use `multiple cores <http://wiki.apache.org/solr/CoreAdmin>`_ and corresponding Haystack
+backends using the language name. Under this scenario, queries are simple::
+
+ sqs = SearchQuerySet.using(lang).auto_query(…)
+
+During index updates, the Index's ``index_queryset`` method will need to filter
+the items to avoid sending the wrong content to the search engine::
+
+ def index_queryset(self, using=None):
+ return Post.objects.filter(language=using)
View
8 docs/searchindex_api.rst
@@ -34,7 +34,7 @@ For the impatient::
def get_model(self):
return Note
- def index_queryset(self):
+ def index_queryset(self, using=None):
"Used when the entire index for model is updated."
return self.get_model().objects.filter(pub_date__lte=datetime.datetime.now())
@@ -386,7 +386,7 @@ This method is required & you must override it to return the correct class.
``index_queryset``
------------------
-.. method:: SearchIndex.index_queryset(self)
+.. method:: SearchIndex.index_queryset(self, using=None)
Get the default QuerySet to index when doing a full update.
@@ -395,7 +395,7 @@ Subclasses can override this method to avoid indexing certain objects.
``read_queryset``
-----------------
-.. method:: SearchIndex.read_queryset(self)
+.. method:: SearchIndex.read_queryset(self, using=None)
Get the default QuerySet for read actions.
@@ -609,7 +609,7 @@ For the impatient::
fields = ['user', 'pub_date']
# Note that regular ``SearchIndex`` methods apply.
- def index_queryset(self):
+ def index_queryset(self, using=None):
"Used when the entire index for model is updated."
return Note.objects.filter(pub_date__lte=datetime.datetime.now())
View
2  docs/tutorial.rst
@@ -221,7 +221,7 @@ Haystack to automatically pick it up. The ``NoteIndex`` should look like::
def get_model(self):
return Note
- def index_queryset(self):
+ def index_queryset(self, using=None):
"""Used when the entire index for model is updated."""
return self.get_model().objects.filter(pub_date__lte=datetime.datetime.now())
View
2  example_project/regular_app/search_indexes.py
@@ -19,7 +19,7 @@ class DogIndex(indexes.SearchIndex, indexes.Indexable):
def get_model(self):
return Dog
- def index_queryset(self):
+ def index_queryset(self, using=None):
return self.get_model().objects.filter(public=True)
def prepare_toys(self, obj):
View
12 haystack/indexes.py
@@ -76,7 +76,7 @@ class NoteIndex(indexes.SearchIndex, indexes.Indexable):
def get_model(self):
return Note
- def index_queryset(self):
+ def index_queryset(self, using=None):
return self.get_model().objects.filter(pub_date__lte=datetime.datetime.now())
"""
@@ -102,7 +102,7 @@ def get_model(self):
"""
raise NotImplementedError("You must provide a 'model' method for the '%r' index." % self)
- def index_queryset(self):
+ def index_queryset(self, using=None):
"""
Get the default QuerySet to index when doing a full update.
@@ -110,16 +110,16 @@ def index_queryset(self):
"""
return self.get_model()._default_manager.all()
- def read_queryset(self):
+ def read_queryset(self, using=None):
"""
Get the default QuerySet for read actions.
Subclasses can override this method to work with other managers.
Useful when working with default managers that filter some objects.
"""
- return self.index_queryset()
+ return self.index_queryset(using=using)
- def build_queryset(self, start_date=None, end_date=None):
+ def build_queryset(self, using=None, start_date=None, end_date=None):
"""
Get the default QuerySet to index when doing an index update.
@@ -154,7 +154,7 @@ def build_queryset(self, start_date=None, end_date=None):
warnings.warn("'SearchIndex.get_queryset' was deprecated in Haystack v2. Please rename the method 'index_queryset'.")
index_qs = self.get_queryset()
else:
- index_qs = self.index_queryset()
+ index_qs = self.index_queryset(using=using)
if not hasattr(index_qs, 'filter'):
raise ImproperlyConfigured("The '%r' class must return a 'QuerySet' in the 'index_queryset' method." % self)
View
34 haystack/management/commands/clear_index.py
@@ -1,7 +1,7 @@
from optparse import make_option
import sys
+
from django.core.management.base import BaseCommand
-from haystack.constants import DEFAULT_ALIAS
class Command(BaseCommand):
@@ -10,35 +10,41 @@ class Command(BaseCommand):
make_option('--noinput', action='store_false', dest='interactive', default=True,
help='If provided, no prompts will be issued to the user and the data will be wiped out.'
),
- make_option("-u", "--using", action="store", type="string", dest="using", default=DEFAULT_ALIAS,
- help='If provided, chooses a connection to work with.'
+ make_option("-u", "--using", action="append", dest="using",
+ default=[],
+ help='Update only the named backend (can be used multiple times). '
+ 'By default all backends will be updated.'
),
)
option_list = BaseCommand.option_list + base_options
-
+
def handle(self, **options):
"""Clears out the search index completely."""
from haystack import connections
self.verbosity = int(options.get('verbosity', 1))
- self.using = options.get('using')
-
+
+ using = options.get('using')
+ if not using:
+ using = connections.connections_info.keys()
+
if options.get('interactive', True):
print
- print "WARNING: This will irreparably remove EVERYTHING from your search index in connection '%s'." % self.using
+ print "WARNING: This will irreparably remove EVERYTHING from your search index in connection '%s'." % "', '".join(using)
print "Your choices after this are to restore from backups or rebuild via the `rebuild_index` command."
-
+
yes_or_no = raw_input("Are you sure you wish to continue? [y/N] ")
print
-
+
if not yes_or_no.lower().startswith('y'):
print "No action taken."
sys.exit()
-
+
if self.verbosity >= 1:
print "Removing all documents from your index because you said so."
-
- backend = connections[self.using].get_backend()
- backend.clear()
-
+
+ for backend_name in using:
+ backend = connections[backend_name].get_backend()
+ backend.clear()
+
if self.verbosity >= 1:
print "All documents removed."
View
48 haystack/management/commands/update_index.py
@@ -1,7 +1,7 @@
from datetime import timedelta
from optparse import make_option
+import logging
import os
-import warnings
from django import db
from django.conf import settings
@@ -11,7 +11,6 @@
from django.utils.encoding import smart_str
from haystack import connections as haystack_connections
-from haystack.constants import DEFAULT_ALIAS
from haystack.query import SearchQuerySet
try:
@@ -71,9 +70,9 @@ def do_update(backend, index, qs, start, end, total, verbosity=1):
if verbosity >= 2:
if hasattr(os, 'getppid') and os.getpid() == os.getppid():
- print " indexed %s - %d of %d." % (start+1, end, total)
+ print " indexed %s - %d of %d." % (start + 1, end, total)
else:
- print " indexed %s - %d of %d (by %s)." % (start+1, end, total, os.getpid())
+ print " indexed %s - %d of %d (by %s)." % (start + 1, end, total, os.getpid())
# FIXME: Get the right backend.
backend.update(index, current_qs)
@@ -121,8 +120,10 @@ class Command(LabelCommand):
make_option('-r', '--remove', action='store_true', dest='remove',
default=False, help='Remove objects from the index that are no longer present in the database.'
),
- make_option("-u", "--using", action="store", type="string", dest="using", default=DEFAULT_ALIAS,
- help='If provided, chooses a connection to work with.'
+ make_option("-u", "--using", action="append", dest="using",
+ default=[],
+ help='Update only the named backend (can be used multiple times). '
+ 'By default all backends will be updated.'
),
make_option('-k', '--workers', action='store', dest='workers',
default=0, type='int',
@@ -137,9 +138,11 @@ def handle(self, *items, **options):
self.start_date = None
self.end_date = None
self.remove = options.get('remove', False)
- self.using = options.get('using')
self.workers = int(options.get('workers', 0))
- self.backend = haystack_connections[self.using].get_backend()
+
+ self.backends = options.get('using')
+ if not self.backends:
+ self.backends = haystack_connections.connections_info.keys()
age = options.get('age', DEFAULT_AGE)
start_date = options.get('start_date')
@@ -202,9 +205,18 @@ def get_models(self, label):
return [get_model(app_label, model_name)]
def handle_label(self, label, **options):
+ for using in self.backends:
+ try:
+ self.update_backend(label, using)
+ except:
+ logging.exception("Error updating %s using %s ", label, using)
+ raise
+
+ def update_backend(self, label, using):
from haystack.exceptions import NotHandled
- unified_index = haystack_connections[self.using].get_unified_index()
+ backend = haystack_connections[using].get_backend()
+ unified_index = haystack_connections[using].get_unified_index()
if self.workers > 0:
import multiprocessing
@@ -218,17 +230,21 @@ def handle_label(self, label, **options):
continue
if self.workers > 0:
- # workers resetting connections leads to references to models / connections getting stale and having their connection disconnected from under them. Resetting before the loop continues and it accesses the ORM makes it better.
+ # workers resetting connections leads to references to models / connections getting
+ # stale and having their connection disconnected from under them. Resetting before
+ # the loop continues and it accesses the ORM makes it better.
db.close_connection()
- qs = index.build_queryset(start_date=self.start_date, end_date=self.end_date)
+ qs = index.build_queryset(using=using, start_date=self.start_date,
+ end_date=self.end_date)
+
total = qs.count()
if self.verbosity >= 1:
print "Indexing %d %s." % (total, smart_str(model._meta.verbose_name_plural))
pks_seen = set([smart_str(pk) for pk in qs.values_list('pk', flat=True)])
- batch_size = self.batchsize or self.backend.batch_size
+ batch_size = self.batchsize or backend.batch_size
if self.workers > 0:
ghetto_queue = []
@@ -237,9 +253,9 @@ def handle_label(self, label, **options):
end = min(start + batch_size, total)
if self.workers == 0:
- do_update(self.backend, index, qs, start, end, total, self.verbosity)
+ do_update(backend, index, qs, start, end, total, self.verbosity)
else:
- ghetto_queue.append(('do_update', model, start, end, total, self.using, self.start_date, self.end_date, self.verbosity))
+ ghetto_queue.append(('do_update', model, start, end, total, using, self.start_date, self.end_date, self.verbosity))
if self.workers > 0:
pool = multiprocessing.Pool(self.workers)
@@ -261,9 +277,9 @@ def handle_label(self, label, **options):
upper_bound = start + batch_size
if self.workers == 0:
- do_remove(self.backend, index, model, pks_seen, start, upper_bound)
+ do_remove(backend, index, model, pks_seen, start, upper_bound)
else:
- ghetto_queue.append(('do_remove', model, pks_seen, start, upper_bound, self.using, self.verbosity))
+ ghetto_queue.append(('do_remove', model, pks_seen, start, upper_bound, using, self.verbosity))
if self.workers > 0:
pool = multiprocessing.Pool(self.workers)
View
2  haystack/query.py
@@ -205,7 +205,7 @@ def post_process_results(self, results):
try:
ui = connections[self.query._using].get_unified_index()
index = ui.get_index(model)
- objects = index.read_queryset()
+ objects = index.read_queryset(using=self.query._using)
loaded_objects[model] = objects.in_bulk(models_pks[model])
except NotHandled:
self.log.warning("Model '%s.%s' not handled by the routers.", self.app_label, self.model_name)
View
1  tests/core/tests/__init__.py
@@ -14,3 +14,4 @@
from core.tests.templatetags import *
from core.tests.views import *
from core.tests.utils import *
+from core.tests.management_commands import *
View
12 tests/core/tests/indexes.py
@@ -67,10 +67,10 @@ def load_all_queryset(self):
def get_model(self):
return MockModel
- def index_queryset(self):
+ def index_queryset(self, using=None):
return MockModel.objects.all()
- def read_queryset(self):
+ def read_queryset(self, using=None):
return MockModel.objects.filter(author__in=['daniel1', 'daniel3'])
def build_queryset(self, start_date=None, end_date=None):
@@ -525,11 +525,11 @@ class GhettoAFifthMockModelSearchIndex(indexes.SearchIndex, indexes.Indexable):
def get_model(self):
return AFifthMockModel
- def index_queryset(self):
+ def index_queryset(self, using=None):
# Index everything,
return self.get_model().objects.complete_set()
- def read_queryset(self):
+ def read_queryset(self, using=None):
return self.get_model().objects.all()
@@ -539,7 +539,7 @@ class ReadQuerySetTestSearchIndex(indexes.SearchIndex, indexes.Indexable):
def get_model(self):
return AFifthMockModel
- def read_queryset(self):
+ def read_queryset(self, using=None):
return self.get_model().objects.complete_set()
@@ -549,7 +549,7 @@ class TextReadQuerySetTestSearchIndex(indexes.SearchIndex, indexes.Indexable):
def get_model(self):
return AFifthMockModel
- def read_queryset(self):
+ def read_queryset(self, using=None):
return self.get_model().objects.complete_set()
View
57 tests/core/tests/management_commands.py
@@ -0,0 +1,57 @@
+from mock import patch, call
+
+from django.core.management import call_command
+from django.test import TestCase
+
+__all__ = ['CoreManagementCommandsTestCase']
+
+
+class CoreManagementCommandsTestCase(TestCase):
+ @patch("haystack.management.commands.update_index.Command.update_backend")
+ def test_update_index_default_using(self, m):
+ """update_index uses default index when --using is not present"""
+ call_command('update_index')
+ m.assert_called_with("core", 'default')
+
+ @patch("haystack.management.commands.update_index.Command.update_backend")
+ def test_update_index_using(self, m):
+ """update_index only applies to indexes specified with --using"""
+ call_command('update_index', verbosity=0, using=["eng", "fra"])
+ m.assert_any_call("core", "eng")
+ m.assert_any_call("core", "fra")
+ self.assertTrue(call("core", "default") not in m.call_args_list,
+ "update_index should have been restricted to the index specified with --using")
+
+ @patch("haystack.loading.ConnectionHandler.__getitem__")
+ def test_clear_index_default_using(self, m):
+ """clear_index uses default index when --using is not present"""
+ call_command('clear_index', verbosity=0, interactive=False)
+ m.assert_called_with("default")
+
+ @patch("haystack.loading.ConnectionHandler.__getitem__")
+ def test_clear_index_using(self, m):
+ """clear_index only applies to indexes specified with --using"""
+
+ call_command('clear_index', verbosity=0, interactive=False, using=["eng"])
+ m.assert_called_with("eng")
+ self.assertTrue(m.return_value.get_backend.called, "backend.clear() should be called")
+ self.assertTrue(call("default") not in m.call_args_list,
+ "clear_index should have been restricted to the index specified with --using")
+
+ @patch("haystack.loading.ConnectionHandler.__getitem__")
+ @patch("haystack.management.commands.update_index.Command.update_backend")
+ def test_rebuild_index_default_using(self, m1, m2):
+ """rebuild_index uses default index when --using is not present"""
+
+ call_command('rebuild_index', verbosity=0, interactive=False)
+ m2.assert_called_with("default")
+ m1.assert_any_call("core", "default")
+
+ @patch("haystack.loading.ConnectionHandler.__getitem__")
+ @patch("haystack.management.commands.update_index.Command.update_backend")
+ def test_rebuild_index_using(self, m1, m2):
+ """rebuild_index passes --using to clear_index and update_index"""
+
+ call_command('rebuild_index', verbosity=0, interactive=False, using=["eng"])
+ m2.assert_called_with("eng")
+ m1.assert_any_call("core", "eng")
View
4 tests/multipleindex/models.py
@@ -4,7 +4,7 @@
class Foo(models.Model):
title = models.CharField(max_length=255)
body = models.TextField()
-
+
def __unicode__(self):
return self.title
@@ -12,6 +12,6 @@ def __unicode__(self):
class Bar(models.Model):
author = models.CharField(max_length=255)
content = models.TextField()
-
+
def __unicode__(self):
return self.author
View
6 tests/multipleindex/search_indexes.py
@@ -11,7 +11,11 @@ def get_model(self):
class FooIndex(BaseIndex, indexes.Indexable):
- pass
+ def index_queryset(self, using=None):
+ qs = super(FooIndex, self).index_queryset(using=using)
+ if using == "filtered_whoosh":
+ qs = qs.filter(body__contains="1")
+ return qs
# Import the old way & make sure things don't explode.
View
15 tests/multipleindex/tests.py
@@ -1,13 +1,16 @@
import os
import shutil
+
from django.conf import settings
from django.db import models
from django.test import TestCase
+
from haystack import connections, connection_router
from haystack.exceptions import NotHandled
from haystack.query import SearchQuerySet
from haystack.signals import BaseSignalProcessor, RealtimeSignalProcessor
from haystack.utils.loading import UnifiedIndex
+
from multipleindex.search_indexes import FooIndex
from multipleindex.models import Foo, Bar
@@ -29,6 +32,7 @@ def setUp(self):
self.bi = self.ui.get_index(Bar)
self.solr_backend = connections['default'].get_backend()
self.whoosh_backend = connections['whoosh'].get_backend()
+ self.filtered_whoosh_backend = connections['filtered_whoosh'].get_backend()
foo_1 = Foo.objects.create(
title='Haystack test',
@@ -184,6 +188,17 @@ def test_excluded_indexes(self):
# Should error, since it's not present.
self.assertRaises(NotHandled, wui.get_index, Bar)
+ def test_filtered_index_update(self):
+ for i in ('whoosh', 'filtered_whoosh'):
+ self.fi.clear(using=i)
+ self.fi.update(using=i)
+
+ results = self.whoosh_backend.search('foo')
+ self.assertEqual(results['hits'], 2)
+
+ results = self.filtered_whoosh_backend.search('foo')
+ self.assertEqual(results['hits'], 1, "Filtered backend should only contain one record")
+
class TestSignalProcessor(BaseSignalProcessor):
def setup(self):
View
5 tests/multipleindex_settings.py
@@ -15,6 +15,11 @@
'PATH': mkdtemp(prefix='haystack-multipleindex-whoosh-tests-'),
'EXCLUDED_INDEXES': ['multipleindex.search_indexes.BarIndex'],
},
+ 'filtered_whoosh': {
+ 'ENGINE': 'haystack.backends.whoosh_backend.WhooshEngine',
+ 'PATH': mkdtemp(prefix='haystack-multipleindex-filtered-whoosh-tests-'),
+ 'EXCLUDED_INDEXES': ['multipleindex.search_indexes.BarIndex'],
+ },
}
HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor'
Something went wrong with that request. Please try again.