mirror of
https://github.com/Hopiu/xapian-haystack.git
synced 2026-03-16 22:20:31 +00:00
Added spelling suggestion
This commit is contained in:
parent
60e8925280
commit
a6e7670973
3 changed files with 276 additions and 56 deletions
|
|
@ -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))')
|
||||
|
|
|
|||
|
|
@ -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)))')
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue