Added in tests for complex registration and multiple search engines.

This commit is contained in:
Dave Hall 2011-08-29 11:51:40 +01:00
parent f07ce3080f
commit 87bae74871
3 changed files with 52 additions and 22 deletions

View file

@ -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 = {

View file

@ -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

View file

@ -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"),