mirror of
https://github.com/Hopiu/xapian-haystack.git
synced 2026-04-14 20:00:59 +00:00
Merge pull request #74 from toastdriven/master.
Changes to help bring the Xapian backend up to current.
This commit is contained in:
commit
ef8ca2dfa7
3 changed files with 99 additions and 4 deletions
|
|
@ -15,10 +15,12 @@ from django.test import TestCase
|
|||
from haystack import indexes, sites, backends
|
||||
from haystack.backends.xapian_backend import SearchBackend, SearchQuery, _marshal_value
|
||||
from haystack.exceptions import HaystackError
|
||||
from haystack.models import SearchResult
|
||||
from haystack.query import SearchQuerySet, SQ
|
||||
from haystack.sites import SearchSite
|
||||
|
||||
from core.models import MockTag, MockModel, AnotherMockModel, AFourthMockModel
|
||||
from core.tests.mocks import MockSearchResult
|
||||
|
||||
|
||||
class XapianMockModel(models.Model):
|
||||
|
|
@ -188,6 +190,9 @@ class XapianSearchBackendTestCase(TestCase):
|
|||
self.assertEqual([result.pk for result in self.backend.search(xapian.Query(''))['results']], [1, 2, 3])
|
||||
self.assertEqual(self.backend.search(xapian.Query('indexed'))['hits'], 3)
|
||||
self.assertEqual([result.pk for result in self.backend.search(xapian.Query(''))['results']], [1, 2, 3])
|
||||
|
||||
# Ensure that swapping the ``result_class`` works.
|
||||
self.assertTrue(isinstance(self.backend.search(xapian.Query('indexed'), result_class=MockSearchResult)['results'][0], MockSearchResult))
|
||||
|
||||
def test_search_field_with_punctuation(self):
|
||||
self.backend.update(self.index, self.sample_objs)
|
||||
|
|
@ -301,6 +306,39 @@ class XapianSearchBackendTestCase(TestCase):
|
|||
results = self.backend.more_like_this(self.sample_objs[0], limit_to_registered_models=True)
|
||||
self.assertEqual(results['hits'], 2)
|
||||
self.assertEqual([result.pk for result in results['results']], [3, 2])
|
||||
|
||||
# Ensure that swapping the ``result_class`` works.
|
||||
self.assertTrue(isinstance(self.backend.more_like_this(self.sample_objs[0], result_class=MockSearchResult)['results'][0], MockSearchResult))
|
||||
|
||||
def test_use_correct_site(self):
|
||||
test_site = SearchSite()
|
||||
test_site.register(XapianMockModel, XapianMockSearchIndex)
|
||||
self.backend.update(self.index, self.sample_objs)
|
||||
|
||||
# Make sure that ``_process_results`` uses the right ``site``.
|
||||
self.assertEqual(self.backend.search(xapian.Query('indexed'))['hits'], 3)
|
||||
self.assertEqual([result.pk for result in self.backend.search(xapian.Query('indexed'))['results']], [1, 2, 3])
|
||||
|
||||
self.site.unregister(XapianMockModel)
|
||||
self.assertEqual(len(self.site.get_indexed_models()), 0)
|
||||
self.backend.site = test_site
|
||||
self.assertTrue(len(self.backend.site.get_indexed_models()) > 0)
|
||||
|
||||
# Should still be there, despite the main ``site`` not having that model
|
||||
# registered any longer.
|
||||
self.assertEqual(self.backend.search(xapian.Query('indexed'))['hits'], 3)
|
||||
self.assertEqual([result.pk for result in self.backend.search(xapian.Query('indexed'))['results']], [1, 2, 3])
|
||||
|
||||
# Unregister it on the backend & make sure it takes effect.
|
||||
self.backend.site.unregister(XapianMockModel)
|
||||
self.assertEqual(len(self.backend.site.get_indexed_models()), 0)
|
||||
self.assertEqual(self.backend.search(xapian.Query('indexed'))['hits'], 0)
|
||||
|
||||
# Nuke it & fallback on the main ``site``.
|
||||
self.backend.site = haystack.site
|
||||
self.assertEqual(self.backend.search(xapian.Query('indexed'))['hits'], 0)
|
||||
self.site.register(XapianMockModel, XapianMockSearchIndex)
|
||||
self.assertEqual(self.backend.search(xapian.Query('indexed'))['hits'], 3)
|
||||
|
||||
def test_order_by(self):
|
||||
self.backend.update(self.index, self.sample_objs)
|
||||
|
|
@ -484,6 +522,38 @@ class LiveXapianSearchQueryTestCase(TestCase):
|
|||
settings.DEBUG = old_debug
|
||||
|
||||
|
||||
class LiveXapianSearchQuerySetTestCase(TestCase):
|
||||
"""
|
||||
SearchQuerySet specific tests
|
||||
"""
|
||||
fixtures = ['initial_data.json']
|
||||
|
||||
def setUp(self):
|
||||
super(LiveXapianSearchQuerySetTestCase, self).setUp()
|
||||
|
||||
site = SearchSite()
|
||||
backend = SearchBackend(site=site)
|
||||
index = LiveXapianMockSearchIndex(MockModel, backend=backend)
|
||||
site.register(MockModel, LiveXapianMockSearchIndex)
|
||||
backend.update(index, MockModel.objects.all())
|
||||
|
||||
self.sq = SearchQuery(backend=backend)
|
||||
self.sqs = SearchQuerySet(query=self.sq)
|
||||
|
||||
def test_result_class(self):
|
||||
# Assert that we're defaulting to ``SearchResult``.
|
||||
sqs = self.sqs.all()
|
||||
self.assertTrue(isinstance(sqs[0], SearchResult))
|
||||
|
||||
# Custom class.
|
||||
sqs = self.sqs.result_class(MockSearchResult).all()
|
||||
self.assertTrue(isinstance(sqs[0], MockSearchResult))
|
||||
|
||||
# Reset to default.
|
||||
sqs = self.sqs.result_class(None).all()
|
||||
self.assertTrue(isinstance(sqs[0], SearchResult))
|
||||
|
||||
|
||||
class XapianBoostBackendTestCase(TestCase):
|
||||
def setUp(self):
|
||||
super(XapianBoostBackendTestCase, self).setUp()
|
||||
|
|
|
|||
|
|
@ -159,3 +159,8 @@ class XapianSearchQueryTestCase(TestCase):
|
|||
def test_build_query_with_punctuation(self):
|
||||
self.sq.add_filter(SQ(content='http://www.example.com'))
|
||||
self.assertEqual(str(self.sq.build_query()), u'Xapian::Query((Zhttp://www.example.com OR http://www.example.com))')
|
||||
|
||||
def test_in_filter_values_list(self):
|
||||
self.sq.add_filter(SQ(content='why'))
|
||||
self.sq.add_filter(SQ(title__in=MockModel.objects.values_list('id', flat=True)))
|
||||
self.assertEqual(str(self.sq.build_query()), u'Xapian::Query(((Zwhi OR why) AND (ZXTITLE1 OR XTITLE1 OR ZXTITLE2 OR XTITLE2 OR ZXTITLE3 OR XTITLE3)))')
|
||||
|
|
|
|||
|
|
@ -317,7 +317,7 @@ class SearchBackend(BaseSearchBackend):
|
|||
def search(self, query, sort_by=None, start_offset=0, end_offset=None,
|
||||
fields='', highlight=False, facets=None, date_facets=None,
|
||||
query_facets=None, narrow_queries=None, spelling_query=None,
|
||||
limit_to_registered_models=True, **kwargs):
|
||||
limit_to_registered_models=True, result_class=None, **kwargs):
|
||||
"""
|
||||
Executes the Xapian::query as defined in `query`.
|
||||
|
||||
|
|
@ -354,6 +354,11 @@ class SearchBackend(BaseSearchBackend):
|
|||
and any suggestions for spell correction will be returned as well as
|
||||
the results.
|
||||
"""
|
||||
if not self.site:
|
||||
from haystack import site
|
||||
else:
|
||||
site = self.site
|
||||
|
||||
if xapian.Query.empty(query):
|
||||
return {
|
||||
'results': [],
|
||||
|
|
@ -362,6 +367,9 @@ class SearchBackend(BaseSearchBackend):
|
|||
|
||||
database = self._database()
|
||||
|
||||
if result_class is None:
|
||||
result_class = SearchResult
|
||||
|
||||
if getattr(settings, 'HAYSTACK_INCLUDE_SPELLING', False) is True:
|
||||
spelling_suggestion = self._do_spelling_suggestion(database, query, spelling_query)
|
||||
else:
|
||||
|
|
@ -424,7 +432,7 @@ class SearchBackend(BaseSearchBackend):
|
|||
)
|
||||
}
|
||||
results.append(
|
||||
SearchResult(app_label, module_name, pk, match.percent, **model_data)
|
||||
result_class(app_label, module_name, pk, match.percent, searchsite=site, **model_data)
|
||||
)
|
||||
|
||||
if facets:
|
||||
|
|
@ -443,7 +451,7 @@ class SearchBackend(BaseSearchBackend):
|
|||
|
||||
def more_like_this(self, model_instance, additional_query=None,
|
||||
start_offset=0, end_offset=None,
|
||||
limit_to_registered_models=True, **kwargs):
|
||||
limit_to_registered_models=True, result_class=None, **kwargs):
|
||||
"""
|
||||
Given a model instance, returns a result set of similar documents.
|
||||
|
||||
|
|
@ -473,8 +481,16 @@ class SearchBackend(BaseSearchBackend):
|
|||
|
||||
Finally, processes the resulting matches and returns.
|
||||
"""
|
||||
if not self.site:
|
||||
from haystack import site
|
||||
else:
|
||||
site = self.site
|
||||
|
||||
database = self._database()
|
||||
|
||||
if result_class is None:
|
||||
result_class = SearchResult
|
||||
|
||||
query = xapian.Query(DOCUMENT_ID_TERM_PREFIX + get_identifier(model_instance))
|
||||
|
||||
enquire = xapian.Enquire(database)
|
||||
|
|
@ -521,7 +537,7 @@ class SearchBackend(BaseSearchBackend):
|
|||
for match in matches:
|
||||
app_label, module_name, pk, model_data = pickle.loads(self._get_document_data(database, match.document))
|
||||
results.append(
|
||||
SearchResult(app_label, module_name, pk, match.percent, **model_data)
|
||||
result_class(app_label, module_name, pk, match.percent, searchsite=site, **model_data)
|
||||
)
|
||||
|
||||
return {
|
||||
|
|
@ -948,6 +964,10 @@ class SearchQuery(BaseSearchQuery):
|
|||
expression, term = child
|
||||
field, filter_type = search_node.split_expression(expression)
|
||||
|
||||
# Handle when we've got a ``ValuesListQuerySet``...
|
||||
if hasattr(term, 'values_list'):
|
||||
term = list(term)
|
||||
|
||||
if isinstance(term, (list, tuple)):
|
||||
term = [_marshal_term(t) for t in term]
|
||||
else:
|
||||
|
|
|
|||
Loading…
Reference in a new issue