diff --git a/tests/xapian_tests/tests/xapian_backend.py b/tests/xapian_tests/tests/xapian_backend.py
index ceb3200..136ed99 100644
--- a/tests/xapian_tests/tests/xapian_backend.py
+++ b/tests/xapian_tests/tests/xapian_backend.py
@@ -1,4 +1,5 @@
# Copyright (C) 2009 David Sauve, Trapeze. All rights reserved.
+# Based on original code by Daniel Lindsley as part of the Haystack test suite.
import cPickle as pickle
import datetime
@@ -10,10 +11,12 @@ from django.conf import settings
from django.db import models
from django.test import TestCase
-from haystack import indexes, sites
-from haystack.backends.xapian_backend import SearchBackend, _marshal_value
+from haystack import indexes, sites, backends
+from haystack.backends.xapian_backend import SearchBackend, SearchQuery, _marshal_value
+from haystack.exceptions import HaystackError
+from haystack.query import SearchQuerySet, SQ
-from core.models import MockTag, AnotherMockModel
+from core.models import MockTag, MockModel, AnotherMockModel
class XapianMockModel(models.Model):
@@ -130,7 +133,6 @@ class XapianSearchBackendTestCase(TestCase):
{'flag': u'f', 'name': u'david2', 'text': u'indexed!\n2', 'sites': u"['2', '4', '6']", 'pub_date': u'20090223000000', 'value': u'000000000010', 'id': u'tests.xapianmockmodel.2', 'slug': u'http://example.com/2', 'popularity': '\xb4p', 'django_id': u'2', 'django_ct': u'tests.xapianmockmodel'},
{'flag': u't', 'name': u'david3', 'text': u'indexed!\n3', 'sites': u"['3', '6', '9']", 'pub_date': u'20090222000000', 'value': u'000000000015', 'id': u'tests.xapianmockmodel.3', 'slug': u'http://example.com/3', 'popularity': '\xcb\x98', 'django_id': u'3', 'django_ct': u'tests.xapianmockmodel'}
])
- import pdb; pdb.set_trace()
def test_duplicate_update(self):
self.sb.update(self.msi, self.sample_objs)
@@ -247,18 +249,18 @@ class XapianSearchBackendTestCase(TestCase):
# self.assertEqual(self.sb.search('Index', highlight=True)['hits'], 3)
# self.assertEqual([result.highlighted['text'] for result in self.sb.search('Index', highlight=True)['results']], ['Indexed!\n1', 'Indexed!\n2', 'Indexed!\n3'])
- # def test_spelling_suggestion(self):
- # self.sb.update(self.msi, self.sample_objs)
- # self.assertEqual(len(self.xapian_search('')), 3)
- #
- # self.assertEqual(self.sb.search('indxe')['hits'], 0)
- # self.assertEqual(self.sb.search('indxe')['spelling_suggestion'], 'indexed')
- #
- # self.assertEqual(self.sb.search('indxed')['hits'], 0)
- # self.assertEqual(self.sb.search('indxed')['spelling_suggestion'], 'indexed')
- #
- # self.assertEqual(self.sb.search('indx')['hits'], 0)
- # self.assertEqual(self.sb.search('indx', spelling_query='indexy')['spelling_suggestion'], 'indexed')
+ def test_spelling_suggestion(self):
+ self.sb.update(self.msi, self.sample_objs)
+ self.assertEqual(len(self.xapian_search('')), 3)
+
+ self.assertEqual(self.sb.search(xapian.Query('indxe'))['hits'], 0)
+ self.assertEqual(self.sb.search(xapian.Query('indxe'))['spelling_suggestion'], 'indexed')
+
+ self.assertEqual(self.sb.search(xapian.Query('indxed'))['hits'], 0)
+ self.assertEqual(self.sb.search(xapian.Query('indxed'))['spelling_suggestion'], 'indexed')
+
+ self.assertEqual(self.sb.search(xapian.Query('indx'))['hits'], 0)
+ self.assertEqual(self.sb.search(xapian.Query('indx'), spelling_query='indexy')['spelling_suggestion'], 'indexed')
# def test_more_like_this(self):
# self.sb.update(self.msi, self.sample_objs)
@@ -341,3 +343,185 @@ class XapianSearchBackendTestCase(TestCase):
{'column': 5, 'field_name': 'flag', 'type': 'boolean', 'multi_valued': 'false'},
{'column': 6, 'field_name': 'pub_date', 'type': 'date', 'multi_valued': 'false'},
])
+
+
+# class LiveXapianSearchQueryTestCase(TestCase):
+# fixtures = ['initial_data.json']
+#
+# def setUp(self):
+# super(LiveXapianSearchQueryTestCase, self).setUp()
+#
+# self.sq = SearchQuery(backend=SearchBackend())
+#
+# # Force indexing of the content.
+# for mock in MockModel.objects.all():
+# mock.save()
+#
+# def test_get_spelling(self):
+# self.sq.add_filter(SQ(content='Indexy'))
+# self.assertEqual(self.sq.get_spelling_suggestion(), u'index')
+# self.assertEqual(self.sq.get_spelling_suggestion('indexy'), u'index')
+#
+# def test_log_query(self):
+# from django.conf import settings
+# from haystack import backends
+# backends.reset_search_queries()
+# self.assertEqual(len(backends.queries), 0)
+#
+# # Stow.
+# old_debug = settings.DEBUG
+# settings.DEBUG = False
+#
+# len(self.sq.get_results())
+# self.assertEqual(len(backends.queries), 0)
+#
+# settings.DEBUG = True
+# # Redefine it to clear out the cached results.
+# self.sq = SearchQuery(backend=SearchBackend())
+# self.sq.add_filter(SQ(name='bar'))
+# len(self.sq.get_results())
+# self.assertEqual(len(backends.queries), 1)
+# self.assertEqual(backends.queries[0]['query_string'], 'xapian::Query(XNAMEbar)')
+#
+# # And again, for good measure.
+# self.sq = SearchQuery(backend=SearchBackend())
+# self.sq.add_filter(SQ(name='bar'))
+# self.sq.add_filter(SQ(text='moof'))
+# len(self.sq.get_results())
+# self.assertEqual(len(backends.queries), 2)
+# self.assertEqual(backends.queries[0]['query_string'].get_description(), u'xapian::Query(XNAMEbar)')
+# self.assertEqual(backends.queries[1]['query_string'].get_description(), u'xapian::Query(XNAMEbar AND XTEXTmoof)')
+#
+# # Restore.
+# settings.DEBUG = old_debug
+#
+#
+# class LiveXapianSearchQuerySetTestCase(TestCase):
+# """Used to test actual implementation details of the SearchQuerySet."""
+# fixtures = ['bulk_data.json']
+#
+# def setUp(self):
+# super(LiveXapianSearchQuerySetTestCase, self).setUp()
+#
+# # With the models registered, you get the proper bits.
+# import haystack
+# from haystack.sites import SearchSite
+#
+# # Stow.
+# self.old_debug = settings.DEBUG
+# settings.DEBUG = True
+# self.old_site = haystack.site
+# test_site = SearchSite()
+# test_site.register(MockModel)
+# haystack.site = test_site
+#
+# self.sqs = SearchQuerySet()
+#
+# # Force indexing of the content.
+# for mock in MockModel.objects.all():
+# mock.save()
+#
+# def tearDown(self):
+# # Restore.
+# import haystack
+# haystack.site = self.old_site
+# settings.DEBUG = self.old_debug
+# super(LiveXapianSearchQuerySetTestCase, self).tearDown()
+#
+# def test_load_all(self):
+# sqs = self.sqs.load_all()
+# self.assert_(isinstance(sqs, SearchQuerySet))
+# self.assert_(len(sqs) > 0)
+# self.assertEqual(sqs[0].object.foo, u"Registering indexes in Haystack is very similar to registering models and ``ModelAdmin`` classes in the `Django admin site`_. If you want to override the default indexing behavior for your model you can specify your own ``SearchIndex`` class. This is useful for ensuring that future-dated or non-live content is not indexed and searchable. Our ``Note`` model has a ``pub_date`` field, so let's update our code to include our own ``SearchIndex`` to exclude indexing future-dated notes:")
+#
+# def test_load_all_queryset(self):
+# sqs = self.sqs.load_all()
+# self.assertRaises(HaystackError, sqs.load_all_queryset, MockModel, MockModel.objects.filter(id__gt=1))
+#
+# def test_iter(self):
+# backends.reset_search_queries()
+# self.assertEqual(len(backends.queries), 0)
+# sqs = self.sqs.all()
+# results = [int(result.pk) for result in sqs]
+# self.assertEqual(results, range(1, 24))
+# self.assertEqual(len(backends.queries), 3)
+#
+# def test_slice(self):
+# backends.reset_search_queries()
+# self.assertEqual(len(backends.queries), 0)
+# results = self.sqs.all()
+# self.assertEqual([int(result.pk) for result in results[1:11]], [2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
+# self.assertEqual(len(backends.queries), 1)
+#
+# backends.reset_search_queries()
+# self.assertEqual(len(backends.queries), 0)
+# results = self.sqs.all()
+# self.assertEqual(int(results[21].pk), 22)
+# self.assertEqual(len(backends.queries), 1)
+#
+# def test_manual_iter(self):
+# results = self.sqs.all()
+#
+# backends.reset_search_queries()
+# self.assertEqual(len(backends.queries), 0)
+# results = [int(result.pk) for result in results._manual_iter()]
+# self.assertEqual(results, range(1, 24))
+# self.assertEqual(len(backends.queries), 3)
+#
+# def test_fill_cache(self):
+# backends.reset_search_queries()
+# self.assertEqual(len(backends.queries), 0)
+# results = self.sqs.all()
+# self.assertEqual(len(results._result_cache), 0)
+# self.assertEqual(len(backends.queries), 0)
+# results._fill_cache(0, 10)
+# self.assertEqual(len([result for result in results._result_cache if result is not None]), 10)
+# self.assertEqual(len(backends.queries), 1)
+# results._fill_cache(10, 20)
+# self.assertEqual(len([result for result in results._result_cache if result is not None]), 20)
+# self.assertEqual(len(backends.queries), 2)
+#
+# def test_cache_is_full(self):
+# backends.reset_search_queries()
+# self.assertEqual(len(backends.queries), 0)
+# self.assertEqual(self.sqs._cache_is_full(), False)
+# results = self.sqs.all()
+# fire_the_iterator_and_fill_cache = [result for result in results]
+# self.assertEqual(results._cache_is_full(), True)
+# self.assertEqual(len(backends.queries), 3)
+#
+# def test___and__(self):
+# sqs1 = self.sqs.filter(content='foo')
+# sqs2 = self.sqs.filter(content='bar')
+# sqs = sqs1 & sqs2
+#
+# self.assert_(isinstance(sqs, SearchQuerySet))
+# self.assertEqual(len(sqs.query.query_filter), 2)
+# self.assertEqual(sqs.query.build_query().get_description(), u'Xapian::Query((foo AND bar))')
+#
+# # Now for something more complex...
+# sqs3 = self.sqs.exclude(title='moof').filter(SQ(content='foo') | SQ(content='baz'))
+# sqs4 = self.sqs.filter(content='bar')
+# sqs = sqs3 & sqs4
+#
+# self.assert_(isinstance(sqs, SearchQuerySet))
+# self.assertEqual(len(sqs.query.query_filter), 3)
+# self.assertEqual(sqs.query.build_query().get_description(), u'Xapian::Query((( AND_NOT XTITLEmoof) AND (foo OR baz) AND bar))')
+#
+# def test___or__(self):
+# sqs1 = self.sqs.filter(content='foo')
+# sqs2 = self.sqs.filter(content='bar')
+# sqs = sqs1 | sqs2
+#
+# self.assert_(isinstance(sqs, SearchQuerySet))
+# self.assertEqual(len(sqs.query.query_filter), 2)
+# self.assertEqual(sqs.query.build_query().get_description(), u'Xapian::Query((foo OR bar))')
+#
+# # Now for something more complex...
+# sqs3 = self.sqs.exclude(title='moof').filter(SQ(content='foo') | SQ(content='baz'))
+# sqs4 = self.sqs.filter(content='bar').models(MockModel)
+# sqs = sqs3 | sqs4
+#
+# self.assert_(isinstance(sqs, SearchQuerySet))
+# self.assertEqual(len(sqs.query.query_filter), 2)
+# self.assertEqual(sqs.query.build_query().get_description(), u'Xapian::Query(((( AND_NOT XTITLEmoof) AND (foo OR baz)) OR bar))')
diff --git a/tests/xapian_tests/tests/xapian_query.py b/tests/xapian_tests/tests/xapian_query.py
index bcc02dc..02655f6 100644
--- a/tests/xapian_tests/tests/xapian_query.py
+++ b/tests/xapian_tests/tests/xapian_query.py
@@ -25,85 +25,85 @@ class XapianSearchQueryTestCase(TestCase):
super(XapianSearchQueryTestCase, self).tearDown()
def test_build_query_all(self):
- self.assertEqual(self.sq.build_query().get_description(), 'Xapian::Query()')
+ self.assertEqual(self.sq.build_query().get_description(), u'Xapian::Query()')
def test_build_query_single_word(self):
self.sq.add_filter(SQ(content='hello'))
- self.assertEqual(self.sq.build_query().get_description(), 'Xapian::Query(hello)')
+ self.assertEqual(self.sq.build_query().get_description(), u'Xapian::Query(hello)')
def test_build_query_single_word_not(self):
self.sq.add_filter(~SQ(content='hello'))
- self.assertEqual(self.sq.build_query().get_description(), 'Xapian::Query(( AND_NOT hello))')
+ self.assertEqual(self.sq.build_query().get_description(), u'Xapian::Query(( AND_NOT hello))')
def test_build_query_single_word_field_exact(self):
self.sq.add_filter(SQ(foo='hello'))
- self.assertEqual(self.sq.build_query().get_description(), 'Xapian::Query(XFOOhello)')
+ self.assertEqual(self.sq.build_query().get_description(), u'Xapian::Query(XFOOhello)')
def test_build_query_single_word_field_exact_not(self):
self.sq.add_filter(~SQ(foo='hello'))
- self.assertEqual(self.sq.build_query().get_description(), 'Xapian::Query(( AND_NOT XFOOhello))')
+ self.assertEqual(self.sq.build_query().get_description(), u'Xapian::Query(( AND_NOT XFOOhello))')
def test_build_query_boolean(self):
self.sq.add_filter(SQ(content=True))
- self.assertEqual(self.sq.build_query().get_description(), 'Xapian::Query(true)')
+ self.assertEqual(self.sq.build_query().get_description(), u'Xapian::Query(true)')
def test_build_query_date(self):
self.sq.add_filter(SQ(content=datetime.date(2009, 5, 8)))
- self.assertEqual(self.sq.build_query().get_description(), 'Xapian::Query(20090508000000)')
+ self.assertEqual(self.sq.build_query().get_description(), u'Xapian::Query(20090508000000)')
def test_build_query_datetime(self):
self.sq.add_filter(SQ(content=datetime.datetime(2009, 5, 8, 11, 28)))
- self.assertEqual(self.sq.build_query().get_description(), 'Xapian::Query(20090508112800)')
+ self.assertEqual(self.sq.build_query().get_description(), u'Xapian::Query(20090508112800)')
def test_build_query_float(self):
self.sq.add_filter(SQ(content=25.52))
- self.assertEqual(self.sq.build_query().get_description(), 'Xapian::Query(25.52)')
+ self.assertEqual(self.sq.build_query().get_description(), u'Xapian::Query(25.52)')
def test_build_query_multiple_words_and(self):
self.sq.add_filter(SQ(content='hello'))
self.sq.add_filter(SQ(content='world'))
- self.assertEqual(self.sq.build_query().get_description(), 'Xapian::Query((hello AND world))')
+ self.assertEqual(self.sq.build_query().get_description(), u'Xapian::Query((hello AND world))')
def test_build_query_multiple_words_not(self):
self.sq.add_filter(~SQ(content='hello'))
self.sq.add_filter(~SQ(content='world'))
- self.assertEqual(self.sq.build_query().get_description(), 'Xapian::Query((( AND_NOT hello) AND ( AND_NOT world)))')
+ self.assertEqual(self.sq.build_query().get_description(), u'Xapian::Query((( AND_NOT hello) AND ( AND_NOT world)))')
def test_build_query_multiple_words_or(self):
self.sq.add_filter(SQ(content='hello') | SQ(content='world'))
- self.assertEqual(self.sq.build_query().get_description(), 'Xapian::Query((hello OR world))')
+ self.assertEqual(self.sq.build_query().get_description(), u'Xapian::Query((hello OR world))')
def test_build_query_multiple_words_or_not(self):
self.sq.add_filter(~SQ(content='hello') | ~SQ(content='world'))
- self.assertEqual(self.sq.build_query().get_description(), 'Xapian::Query((( AND_NOT hello) OR ( AND_NOT world)))')
+ self.assertEqual(self.sq.build_query().get_description(), u'Xapian::Query((( AND_NOT hello) OR ( AND_NOT world)))')
def test_build_query_multiple_words_mixed(self):
self.sq.add_filter(SQ(content='why') | SQ(content='hello'))
self.sq.add_filter(~SQ(content='world'))
- self.assertEqual(self.sq.build_query().get_description(), 'Xapian::Query(((why OR hello) AND ( AND_NOT world)))')
+ self.assertEqual(self.sq.build_query().get_description(), u'Xapian::Query(((why OR hello) AND ( AND_NOT world)))')
def test_build_query_multiple_word_field_exact(self):
self.sq.add_filter(SQ(foo='hello'))
self.sq.add_filter(SQ(bar='world'))
- self.assertEqual(self.sq.build_query().get_description(), 'Xapian::Query((XFOOhello AND XBARworld))')
+ self.assertEqual(self.sq.build_query().get_description(), u'Xapian::Query((XFOOhello AND XBARworld))')
def test_build_query_multiple_word_field_exact_not(self):
self.sq.add_filter(~SQ(foo='hello'))
self.sq.add_filter(~SQ(bar='world'))
- self.assertEqual(self.sq.build_query().get_description(), 'Xapian::Query((( AND_NOT XFOOhello) AND ( AND_NOT XBARworld)))')
+ self.assertEqual(self.sq.build_query().get_description(), u'Xapian::Query((( AND_NOT XFOOhello) AND ( AND_NOT XBARworld)))')
def test_build_query_phrase(self):
self.sq.add_filter(SQ(content='hello world'))
- self.assertEqual(self.sq.build_query().get_description(), 'Xapian::Query((hello PHRASE 2 world))')
+ self.assertEqual(self.sq.build_query().get_description(), u'Xapian::Query((hello PHRASE 2 world))')
def test_build_query_phrase_not(self):
self.sq.add_filter(~SQ(content='hello world'))
- self.assertEqual(self.sq.build_query().get_description(), 'Xapian::Query(( AND_NOT (hello PHRASE 2 world)))')
+ self.assertEqual(self.sq.build_query().get_description(), u'Xapian::Query(( AND_NOT (hello PHRASE 2 world)))')
def test_build_query_boost(self):
self.sq.add_filter(SQ(content='hello'))
self.sq.add_boost('world', 5)
- self.assertEqual(self.sq.build_query().get_description(), 'Xapian::Query((hello OR 5 * world))')
+ self.assertEqual(self.sq.build_query().get_description(), u'Xapian::Query((hello OR 5 * world))')
# def test_build_query_multiple_filter_types(self):
# self.sq.add_filter(SQ(content='why'))
@@ -112,37 +112,38 @@ class XapianSearchQueryTestCase(TestCase):
# self.sq.add_filter(SQ(created__lt='2009-02-12 12:13:00'))
# self.sq.add_filter(SQ(title__gte='B'))
# self.sq.add_filter(SQ(id__in=[1, 2, 3]))
- # self.assertEqual(self.sq.build_query().get_description(), 'Xapian::Query(why AND pub_date:[* TO "2009-02-10 01:59:00"] AND author:{david TO *} AND created:{* TO "2009-02-12 12:13:00"} AND title:[B TO *] AND (id:"1" OR id:"2" OR id:"3"))')
+ # self.assertEqual(self.sq.build_query().get_description(), u'Xapian::Query(why AND pub_date:[* TO "2009-02-10 01:59:00"] AND author:{david TO *} AND created:{* TO "2009-02-12 12:13:00"} AND title:[B TO *] AND (id:"1" OR id:"2" OR id:"3"))')
def test_build_query_in_filter_single_words(self):
self.sq.add_filter(SQ(content='why'))
self.sq.add_filter(SQ(title__in=["Dune", "Jaws"]))
- self.assertEqual(self.sq.build_query().get_description(), 'Xapian::Query((why AND (XTITLEdune OR XTITLEjaws)))')
+ self.assertEqual(self.sq.build_query().get_description(), u'Xapian::Query((why AND (XTITLEdune OR XTITLEjaws)))')
def test_build_query_not_in_filter_single_words(self):
self.sq.add_filter(SQ(content='why'))
self.sq.add_filter(~SQ(title__in=["Dune", "Jaws"]))
- self.assertEqual(self.sq.build_query().get_description(), 'Xapian::Query((why AND ( AND_NOT (XTITLEdune OR XTITLEjaws))))')
+ self.assertEqual(self.sq.build_query().get_description(), u'Xapian::Query((why AND ( AND_NOT (XTITLEdune OR XTITLEjaws))))')
def test_build_query_in_filter_multiple_words(self):
self.sq.add_filter(SQ(content='why'))
self.sq.add_filter(SQ(title__in=["A Famous Paper", "An Infamous Article"]))
- self.assertEqual(self.sq.build_query().get_description(), 'Xapian::Query((why AND ((XTITLEa PHRASE 3 XTITLEfamous PHRASE 3 XTITLEpaper) OR (XTITLEan PHRASE 3 XTITLEinfamous PHRASE 3 XTITLEarticle))))')
+ self.assertEqual(self.sq.build_query().get_description(), u'Xapian::Query((why AND ((XTITLEa PHRASE 3 XTITLEfamous PHRASE 3 XTITLEpaper) OR (XTITLEan PHRASE 3 XTITLEinfamous PHRASE 3 XTITLEarticle))))')
def test_build_query_not_in_filter_multiple_words(self):
self.sq.add_filter(SQ(content='why'))
self.sq.add_filter(~SQ(title__in=["A Famous Paper", "An Infamous Article"]))
- self.assertEqual(self.sq.build_query().get_description(), 'Xapian::Query((why AND ( AND_NOT ((XTITLEa PHRASE 3 XTITLEfamous PHRASE 3 XTITLEpaper) OR (XTITLEan PHRASE 3 XTITLEinfamous PHRASE 3 XTITLEarticle)))))')
+ self.assertEqual(self.sq.build_query().get_description(), u'Xapian::Query((why AND ( AND_NOT ((XTITLEa PHRASE 3 XTITLEfamous PHRASE 3 XTITLEpaper) OR (XTITLEan PHRASE 3 XTITLEinfamous PHRASE 3 XTITLEarticle)))))')
def test_build_query_in_filter_datetime(self):
self.sq.add_filter(SQ(content='why'))
self.sq.add_filter(SQ(pub_date__in=[datetime.datetime(2009, 7, 6, 1, 56, 21)]))
- self.assertEqual(self.sq.build_query().get_description(), 'Xapian::Query((why AND XPUB_DATE20090706015621))')
+ self.assertEqual(self.sq.build_query().get_description(), u'Xapian::Query((why AND XPUB_DATE20090706015621))')
# def test_build_query_wildcard_filter_types(self):
# self.sq.add_filter(SQ(content='why'))
# self.sq.add_filter(SQ(title__startswith='haystack'))
- # self.assertEqual(self.sq.build_query().get_description(), 'Xapian::Query(why AND XTITLEhaystack*)')
+ # self.assertEqual(self.sq.build_query().get_description(), 'Xapian::Query((why AND XTITLEhaystack))')
+ # Because wildcards are expanded using existing documents, a more thorough test for this is performed in SearchBackend tests
# def test_stem_single_word(self):
# self.sq.add_filter(SQ(content='testing'))
@@ -157,7 +158,7 @@ class XapianSearchQueryTestCase(TestCase):
def test_build_query_with_models(self):
self.sq.add_filter(SQ(content='hello'))
self.sq.add_model(MockModel)
- self.assertEqual(self.sq.build_query().get_description(), 'Xapian::Query((hello AND 0 * XCONTENTTYPEcore.mockmodel))')
+ self.assertEqual(self.sq.build_query().get_description(), u'Xapian::Query((hello AND 0 * XCONTENTTYPEcore.mockmodel))')
self.sq.add_model(AnotherMockModel)
- self.assertEqual(self.sq.build_query().get_description(), 'Xapian::Query((hello AND (0 * XCONTENTTYPEcore.anothermockmodel OR 0 * XCONTENTTYPEcore.mockmodel)))')
+ self.assertEqual(self.sq.build_query().get_description(), u'Xapian::Query((hello AND (0 * XCONTENTTYPEcore.anothermockmodel OR 0 * XCONTENTTYPEcore.mockmodel)))')
diff --git a/xapian_backend.py b/xapian_backend.py
index 63c9aa7..1b8480b 100755
--- a/xapian_backend.py
+++ b/xapian_backend.py
@@ -139,8 +139,8 @@ class SearchBackend(BaseSearchBackend):
if field['field_name'] in data.keys():
prefix = DOCUMENT_CUSTOM_TERM_PREFIX + field['field_name'].upper()
value = data[field['field_name']]
- term_generator.index_text(force_unicode(value))
- term_generator.index_text(force_unicode(value), 1, prefix)
+ term_generator.index_text(_marshal_term(value))
+ term_generator.index_text(_marshal_term(value), 1, prefix)
document.add_value(field['column'], _marshal_value(value))
document.set_data(pickle.dumps(
@@ -266,10 +266,10 @@ class SearchBackend(BaseSearchBackend):
database = self._database()
- # query, spelling_suggestion = self._query(
- # database, query_string, narrow_queries, spelling_query
- # )
- spelling_suggestion = ''
+ if getattr(settings, 'HAYSTACK_INCLUDE_SPELLING', False) is True:
+ spelling_suggestion = self._do_spelling_suggestion(database, query, spelling_query)
+ else:
+ spelling_suggestion = ''
enquire = xapian.Enquire(database)
enquire.set_query(query)
@@ -609,6 +609,26 @@ class SearchBackend(BaseSearchBackend):
return facet_dict
+ def _do_spelling_suggestion(self, database, query, spelling_query):
+ """
+ Private method that returns a single spelling suggestion based on
+ `spelling_query` or `query`.
+
+ Required arguments:
+ `database` -- The database to check spelling against
+ `query` -- The query to check
+ `spelling_query` -- If not None, this will be checked instead of `query`
+
+ Returns a string with a suggested spelling
+ """
+ if spelling_query:
+ if ' ' in spelling_query:
+ return ' '.join([database.get_spelling_suggestion(term) for term in spelling_query.split()])
+ else:
+ return database.get_spelling_suggestion(spelling_query)
+
+ return ' '.join([database.get_spelling_suggestion(term) for term in query])
+
def _database(self, writable=False):
"""
Private method that returns a xapian.Database for use and sets up
@@ -753,7 +773,7 @@ class SearchQuery(BaseSearchQuery):
elif filter_type == 'lte':
pass
elif filter_type == 'startswith':
- pass
+ query_list.append(self._filter_startswith(term, field, is_not))
elif filter_type == 'in':
query_list.append(self._filter_in(term, field, is_not))
@@ -845,6 +865,26 @@ class SearchQuery(BaseSearchQuery):
else:
return xapian.Query(xapian.Query.OP_OR, query_list)
+ def _filter_startswith(self, term, field, is_not):
+ """
+ Private method that returns a xapian.Query that searches for any term
+ that begins with `term` in a specified `field`.
+
+ Required arguments:
+ ``term`` -- The terms to search for
+ ``field`` -- The field to search
+ ``is_not`` -- Invert the search results
+
+ Returns:
+ A xapian.Query
+ """
+ sb = SearchBackend()
+ for t in sb._database().allterms():
+ print t
+ term_list = [term, 'foo']
+ return self._filter_in(term_list, field, is_not)
+
+
def _all_query(self):
"""
Private method that returns a xapian.Query that returns all documents,
@@ -928,11 +968,6 @@ def _marshal_term(term):
term = _marshal_datetime(term)
elif isinstance(term, datetime.date):
term = _marshal_date(term)
- # elif isinstance(term, bool):
- # if term:
- # term = u'true'
- # else:
- # term = u'false'
else:
term = force_unicode(term).lower()
return term