Added spelling suggestion

This commit is contained in:
David Sauve 2009-12-03 12:20:04 -05:00
parent 60e8925280
commit a6e7670973
3 changed files with 276 additions and 56 deletions

View file

@ -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']], ['<em>Index</em>ed!\n1', '<em>Index</em>ed!\n2', '<em>Index</em>ed!\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(((<alldocuments> 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((((<alldocuments> AND_NOT XTITLEmoof) AND (foo OR baz)) OR bar))')

View file

@ -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(<alldocuments>)')
self.assertEqual(self.sq.build_query().get_description(), u'Xapian::Query(<alldocuments>)')
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((<alldocuments> AND_NOT hello))')
self.assertEqual(self.sq.build_query().get_description(), u'Xapian::Query((<alldocuments> 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((<alldocuments> AND_NOT XFOOhello))')
self.assertEqual(self.sq.build_query().get_description(), u'Xapian::Query((<alldocuments> 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(((<alldocuments> AND_NOT hello) AND (<alldocuments> AND_NOT world)))')
self.assertEqual(self.sq.build_query().get_description(), u'Xapian::Query(((<alldocuments> AND_NOT hello) AND (<alldocuments> 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(((<alldocuments> AND_NOT hello) OR (<alldocuments> AND_NOT world)))')
self.assertEqual(self.sq.build_query().get_description(), u'Xapian::Query(((<alldocuments> AND_NOT hello) OR (<alldocuments> 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 (<alldocuments> AND_NOT world)))')
self.assertEqual(self.sq.build_query().get_description(), u'Xapian::Query(((why OR hello) AND (<alldocuments> 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(((<alldocuments> AND_NOT XFOOhello) AND (<alldocuments> AND_NOT XBARworld)))')
self.assertEqual(self.sq.build_query().get_description(), u'Xapian::Query(((<alldocuments> AND_NOT XFOOhello) AND (<alldocuments> 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((<alldocuments> AND_NOT (hello PHRASE 2 world)))')
self.assertEqual(self.sq.build_query().get_description(), u'Xapian::Query((<alldocuments> 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 (<alldocuments> AND_NOT (XTITLEdune OR XTITLEjaws))))')
self.assertEqual(self.sq.build_query().get_description(), u'Xapian::Query((why AND (<alldocuments> 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 (<alldocuments> 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 (<alldocuments> 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)))')

View file

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