From 87bae74871cb4a74e2bbdc8afea2ba3dcbebbe27 Mon Sep 17 00:00:00 2001 From: Dave Hall Date: Mon, 29 Aug 2011 11:51:40 +0100 Subject: [PATCH] Added in tests for complex registration and multiple search engines. --- src/watson/backends.py | 26 ++++++++++++------------- src/watson/registration.py | 8 ++++---- src/watson/tests.py | 40 +++++++++++++++++++++++++++++++++----- 3 files changed, 52 insertions(+), 22 deletions(-) diff --git a/src/watson/backends.py b/src/watson/backends.py index f034a51..24decf6 100644 --- a/src/watson/backends.py +++ b/src/watson/backends.py @@ -1,6 +1,6 @@ """Search backends used by django-watson.""" -import re, operator +import re from django.conf import settings from django.core.exceptions import ImproperlyConfigured @@ -28,7 +28,7 @@ class SearchBackend(object): supports_ranking = False - def do_search(self, queryset, search_text): + def do_search(self, engine_slug, queryset, search_text): """Filters the given queryset according the the search logic for this backend.""" word_queries = [] for word in search_text.split(): @@ -39,7 +39,7 @@ class SearchBackend(object): word_query ) - def do_search_ranking(self, queryset, search_text): + def do_search_ranking(self, engine_slug, queryset, search_text): """Ranks the given queryset according to the relevance of the given search text.""" return queryset.extra( select = { @@ -47,18 +47,17 @@ class SearchBackend(object): }, ) - def do_filter(self, queryset, search_text): + def do_filter(self, engine_slug, queryset, search_text): """Filters the given queryset according the the search logic for this backend.""" - word_queries = [] + word_query = Q(searchentry_set__engine_slug=engine_slug) for word in search_text.split(): regex = regex_from_word(word) - word_queries.append(Q(searchentry_set__title__iregex=regex) | Q(searchentry_set__content__iregex=regex) | Q(searchentry_set__content__iregex=regex)) - word_query = reduce(operator.and_, word_queries) + word_query &= (Q(searchentry_set__title__iregex=regex) | Q(searchentry_set__content__iregex=regex) | Q(searchentry_set__content__iregex=regex)) return queryset.filter( word_query ) - def do_filter_ranking(self, queryset, search_text): + def do_filter_ranking(self, engine_slug, queryset, search_text): """Ranks the given queryset according to the relevance of the given search text.""" return queryset.extra( select = { @@ -114,14 +113,14 @@ class PostgresSearchBackend(SearchBackend): supports_ranking = True - def do_search(self, queryset, search_text): + def do_search(self, engine_slug, queryset, search_text): """Performs the full text search.""" return queryset.extra( where = ("search_tsv @@ plainto_tsquery(%s)",), params = (search_text,), ) - def do_search_ranking(self, queryset, search_text): + def do_search_ranking(self, engine_slug, queryset, search_text): """Performs full text ranking.""" return queryset.extra( select = { @@ -131,7 +130,7 @@ class PostgresSearchBackend(SearchBackend): order_by = ("-watson_rank",), ) - def do_filter(self, queryset, search_text): + def do_filter(self, engine_slug, queryset, search_text): """Performs the full text filter.""" model = queryset.model content_type = ContentType.objects.get_for_model(model) @@ -142,6 +141,7 @@ class PostgresSearchBackend(SearchBackend): return queryset.extra( tables = ("watson_searchentry",), where = ( + "watson_searchentry.engine_slug = %s", "watson_searchentry.search_tsv @@ plainto_tsquery(%s)", "watson_searchentry.{ref_name} = {table_name}.{pk_name}".format( ref_name = ref_name, @@ -150,10 +150,10 @@ class PostgresSearchBackend(SearchBackend): ), "watson_searchentry.content_type_id = %s" ), - params = (search_text, content_type.id), + params = (engine_slug, search_text, content_type.id), ) - def do_filter_ranking(self, queryset, search_text): + def do_filter_ranking(self, engine_slug, queryset, search_text): """Performs the full text ranking.""" return queryset.extra( select = { diff --git a/src/watson/registration.py b/src/watson/registration.py index 5e7d55b..512f3c7 100644 --- a/src/watson/registration.py +++ b/src/watson/registration.py @@ -452,10 +452,10 @@ class SearchEngine(object): ) # Perform the backend-specific full text match. backend = get_backend() - queryset = backend.do_search(queryset, search_text) + queryset = backend.do_search(self._engine_slug, queryset, search_text) # Perform the backend-specific full-text ranking. if ranking: - queryset = backend.do_search_ranking(queryset, search_text) + queryset = backend.do_search_ranking(self._engine_slug, queryset, search_text) # Return the complete queryset. return queryset @@ -469,10 +469,10 @@ class SearchEngine(object): queryset = queryset._default_manager.all() # Perform the backend-specific full text match. backend = get_backend() - queryset = backend.do_filter(queryset, search_text) + queryset = backend.do_filter(self._engine_slug, queryset, search_text) # Perform the backend-specific full-text ranking. if ranking: - queryset = backend.do_filter_ranking(queryset, search_text) + queryset = backend.do_filter_ranking(self._engine_slug, queryset, search_text) # Return the complete queryset. return queryset diff --git a/src/watson/tests.py b/src/watson/tests.py index 229a591..697c691 100644 --- a/src/watson/tests.py +++ b/src/watson/tests.py @@ -8,7 +8,7 @@ from django.core.management import call_command from django.conf.urls.defaults import * import watson -from watson.registration import RegistrationError, get_backend +from watson.registration import RegistrationError, get_backend, SearchEngine from watson.models import SearchEntry @@ -85,6 +85,9 @@ class RegistrationTest(TestCase): self.assertRaises(RegistrationError, lambda: isinstance(watson.get_adapter(TestModel1))) +complex_registration_search_engine = SearchEngine("restricted") + + class SearchTestBase(TestCase): live_filter = False @@ -98,6 +101,8 @@ class SearchTestBase(TestCase): # Register the test models. watson.register(TestModel1, live_filter=self.live_filter) watson.register(TestModel2, exclude=("id",), live_filter=self.live_filter) + complex_registration_search_engine.register(TestModel1, exclude=("content", "description",), store=("is_published",)) + complex_registration_search_engine.register(TestModel2, fields=("title",)) # Create some test models. self.test11 = TestModel1.objects.create( title = "title model1 instance11", @@ -127,6 +132,8 @@ class SearchTestBase(TestCase): # Unregister the test models. watson.unregister(TestModel1) watson.unregister(TestModel2) + complex_registration_search_engine.unregister(TestModel1) + complex_registration_search_engine.unregister(TestModel2) # Delete the test models. TestModel1.objects.all().delete() TestModel2.objects.all().delete() @@ -141,7 +148,7 @@ class SearchTestBase(TestCase): class InternalsTest(SearchTestBase): def testSearchEntriesCreated(self): - self.assertEqual(SearchEntry.objects.count(), 4) + self.assertEqual(SearchEntry.objects.filter(engine_slug="default").count(), 4) def testBuildWatsonCommand(self): # This update won't take affect, because no search context is active. @@ -168,16 +175,17 @@ class InternalsTest(SearchTestBase): self.assertEqual(watson.search("foo").count(), 0) def testFixesDuplicateSearchEntries(self): + search_entries = SearchEntry.objects.filter(engine_slug="default") # Duplicate a couple of search entries. - for search_entry in SearchEntry.objects.all()[:2]: + for search_entry in search_entries.all()[:2]: search_entry.id = None search_entry.save() # Make sure that we have six (including duplicates). - self.assertEqual(SearchEntry.objects.count(), 6) + self.assertEqual(search_entries.all().count(), 6) # Run the rebuild command. call_command("buildwatson", verbosity=0) # Make sure that we have four again (including duplicates). - self.assertEqual(SearchEntry.objects.count(), 4) + self.assertEqual(search_entries.all().count(), 4) def testSearchEmailParts(self): with watson.context(): @@ -393,6 +401,28 @@ class RankingTest(SearchTestBase): ) +class ComplexRegistrationTest(SearchTestBase): + + def testMetaStored(self): + self.assertEqual(complex_registration_search_engine.search("instance11")[0].meta["is_published"], True) + + def testMetaNotStored(self): + self.assertRaises(KeyError, lambda: complex_registration_search_engine.search("instance21")[0].meta["is_published"]) + + def testFieldsExcludedOnSearch(self): + self.assertEqual(complex_registration_search_engine.search("TITLE").count(), 4) + self.assertEqual(complex_registration_search_engine.search("CONTENT").count(), 0) + self.assertEqual(complex_registration_search_engine.search("DESCRIPTION").count(), 0) + + def testFieldsExcludedOnFilter(self): + self.assertEqual(complex_registration_search_engine.filter(TestModel1, "TITLE").count(), 2) + self.assertEqual(complex_registration_search_engine.filter(TestModel1, "CONTENT").count(), 0) + self.assertEqual(complex_registration_search_engine.filter(TestModel1, "DESCRIPTION").count(), 0) + self.assertEqual(complex_registration_search_engine.filter(TestModel2, "TITLE").count(), 2) + self.assertEqual(complex_registration_search_engine.filter(TestModel2, "CONTENT").count(), 0) + self.assertEqual(complex_registration_search_engine.filter(TestModel2, "DESCRIPTION").count(), 0) + + urlpatterns = patterns("watson.views", url("^simple/$", "search", name="search_simple"),