def search_products( request, template_name='satchless/search/haystack_predictive/products.html'): form = forms.ProductPredictiveSearchForm(data=request.GET or None) if form.is_valid(): results = form.search() query = form.cleaned_data['q'] else: results = RelatedSearchQuerySet().all() results = results.load_all() results = results.models(Product) query = '' products_per_page = getattr(settings, 'HAYSTACK_PREDICTIVE_PRODUCTS_PER_PAGE', PRODUCTS_PER_PAGE) paginator = Paginator(results, products_per_page) try: page = paginator.page(request.GET.get('page', 1)) except InvalidPage: raise Http404() return direct_to_template(request, template_name, { 'form': form, 'page': page, 'paginator': paginator, 'query': query, })
def plus_search(tags, search, search_types, order, extra_filter=None): items = get_resources_for_tag_intersection(tags) q = None for typ, info in search_types: if info[0]: typ_items = Q(**info[0]) if info[1]: typ_items = typ_items & ~Q(**info[1]) elif info[1]: typ_items = ~Q(**info[1]) if not q: q = typ_items else: q = q | typ_items if extra_filter: q = q & Q(**extra_filter) if q: items = items.filter(q) results_map = {} tag_intersection = [] if search: results = RelatedSearchQuerySet().auto_query(search) results_map = {} if results: all_results = results.load_all() all_results = all_results.load_all_queryset(GenericReference, items) results_map["All"] = [item.object for item in all_results] # we really really shouldn't do this else: results_map = {"All": EmptySearchQuerySet()} else: if items: results_map["All"] = items.all() if "All" in results_map: tag_intersection = get_intersecting_tags(results_map["All"], n=15) if len(search_types) > 1: for typ, info in search_types: if info[0]: typ_items = items.filter(**info[0]) if info[1]: typ_items = typ_items.exclude(**info[1]) elif info[1]: typ_items = items.exclude(**info[1]) if search and results: typ_items = all_results.load_all_queryset(GenericReference, typ_items) typ_items = [item.object for item in typ_items] # we really really shouldn't do this results_map[typ] = typ_items else: results_map = {"All": EmptySearchQuerySet()} search_types = [ (typ, data[2], results_map[typ], len(results_map[typ])) for typ, data in search_types if results_map.has_key(typ) ] return (results_map["All"], search_types, tag_intersection)
def search_products(request, template_name='satchless/search/haystack_predictive/products.html'): form = forms.ProductPredictiveSearchForm(data=request.GET or None) if form.is_valid(): results = form.search() query = form.cleaned_data['q'] else: results = RelatedSearchQuerySet().all() results = results.load_all() results = results.models(Product) query = '' products_per_page = getattr(settings, 'HAYSTACK_PREDICTIVE_PRODUCTS_PER_PAGE', PRODUCTS_PER_PAGE) paginator = Paginator(results, products_per_page) try: page = paginator.page(request.GET.get('page', 1)) except InvalidPage: raise Http404() return TemplateResponse(request, template_name, { 'form': form, 'page': page, 'paginator': paginator, 'query': query, })
class LiveSolrSearchQuerySetTestCase(TestCase): """Used to test actual implementation details of the SearchQuerySet.""" fixtures = ['bulk_data.json'] def setUp(self): super(LiveSolrSearchQuerySetTestCase, self).setUp() # Stow. self.old_debug = settings.DEBUG settings.DEBUG = True self.old_ui = connections['default'].get_unified_index() self.ui = UnifiedIndex() self.smmi = SolrMockSearchIndex() self.ui.build(indexes=[self.smmi]) connections['default']._index = self.ui self.sqs = SearchQuerySet() self.rsqs = RelatedSearchQuerySet() # Ugly but not constantly reindexing saves us almost 50% runtime. global lssqstc_all_loaded if lssqstc_all_loaded is None: print 'Reloading data...' lssqstc_all_loaded = True # Wipe it clean. clear_solr_index() # Force indexing of the content. self.smmi.update() def tearDown(self): # Restore. connections['default']._index = self.old_ui settings.DEBUG = self.old_debug super(LiveSolrSearchQuerySetTestCase, self).tearDown() def test_load_all(self): sqs = self.sqs.load_all() self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertTrue(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_iter(self): reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) sqs = self.sqs.all() results = [int(result.pk) for result in sqs] self.assertEqual(results, range(1, 24)) self.assertEqual(len(connections['default'].queries), 3) def test_slice(self): reset_search_queries() self.assertEqual(len(connections['default'].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(connections['default'].queries), 1) reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) results = self.sqs.all() self.assertEqual(int(results[21].pk), 22) self.assertEqual(len(connections['default'].queries), 1) def test_count(self): reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) sqs = self.sqs.all() self.assertEqual(sqs.count(), 23) self.assertEqual(sqs.count(), 23) self.assertEqual(len(sqs), 23) self.assertEqual(sqs.count(), 23) # Should only execute one query to count the length of the result set. self.assertEqual(len(connections['default'].queries), 1) def test_manual_iter(self): results = self.sqs.all() reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) results = [int(result.pk) for result in results._manual_iter()] self.assertEqual(results, range(1, 24)) self.assertEqual(len(connections['default'].queries), 3) def test_fill_cache(self): reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) results = self.sqs.all() self.assertEqual(len(results._result_cache), 0) self.assertEqual(len(connections['default'].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(connections['default'].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(connections['default'].queries), 2) def test_cache_is_full(self): reset_search_queries() self.assertEqual(len(connections['default'].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(connections['default'].queries), 3) def test___and__(self): sqs1 = self.sqs.filter(content='foo') sqs2 = self.sqs.filter(content='bar') sqs = sqs1 & sqs2 self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.query_filter), 2) self.assertEqual(sqs.query.build_query(), u'((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.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.query_filter), 3) self.assertEqual( sqs.query.build_query(), u'(NOT (title:(moof)) 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.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.query_filter), 2) self.assertEqual(sqs.query.build_query(), u'((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.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.query_filter), 2) self.assertEqual( sqs.query.build_query(), u'((NOT (title:(moof)) AND ((foo) OR (baz))) OR (bar))') def test_auto_query(self): # Ensure bits in exact matches get escaped properly as well. # This will break horrifically if escaping isn't working. sqs = self.sqs.auto_query('"pants:rule"') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(repr(sqs.query.query_filter), '<SQ: AND content__contains="pants:rule">') self.assertEqual(sqs.query.build_query(), u'("pants\\:rule")') self.assertEqual(len(sqs), 0) # Regressions def test_regression_proper_start_offsets(self): sqs = self.sqs.filter(text='index') self.assertNotEqual(sqs.count(), 0) id_counts = {} for item in sqs: if item.id in id_counts: id_counts[item.id] += 1 else: id_counts[item.id] = 1 for key, value in id_counts.items(): if value > 1: self.fail( "Result with id '%s' seen more than once in the results." % key) def test_regression_raw_search_breaks_slicing(self): sqs = self.sqs.raw_search('text: index') page_1 = [result.pk for result in sqs[0:10]] page_2 = [result.pk for result in sqs[10:20]] for pk in page_2: if pk in page_1: self.fail( "Result with id '%s' seen more than once in the results." % pk) # RelatedSearchQuerySet Tests def test_related_load_all(self): sqs = self.rsqs.load_all() self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertTrue(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_related_load_all_queryset(self): sqs = self.rsqs.load_all() self.assertEqual(len(sqs._load_all_querysets), 0) sqs = sqs.load_all_queryset(MockModel, MockModel.objects.filter(id__gt=1)) self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs._load_all_querysets), 1) self.assertEqual([obj.object.id for obj in sqs], range(2, 24)) sqs = sqs.load_all_queryset(MockModel, MockModel.objects.filter(id__gt=10)) self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs._load_all_querysets), 1) self.assertEqual([obj.object.id for obj in sqs], range(11, 24)) self.assertEqual([obj.object.id for obj in sqs[10:20]], [21, 22, 23]) def test_related_iter(self): reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) sqs = self.rsqs.all() results = [int(result.pk) for result in sqs] self.assertEqual(results, range(1, 24)) self.assertEqual(len(connections['default'].queries), 4) def test_related_slice(self): reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) results = self.rsqs.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(connections['default'].queries), 3) reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) results = self.rsqs.all() self.assertEqual(int(results[21].pk), 22) self.assertEqual(len(connections['default'].queries), 4) reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) results = self.rsqs.all() self.assertEqual([int(result.pk) for result in results[20:30]], [21, 22, 23]) self.assertEqual(len(connections['default'].queries), 4) def test_related_manual_iter(self): results = self.rsqs.all() reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) results = [int(result.pk) for result in results._manual_iter()] self.assertEqual(results, range(1, 24)) self.assertEqual(len(connections['default'].queries), 4) def test_related_fill_cache(self): reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) results = self.rsqs.all() self.assertEqual(len(results._result_cache), 0) self.assertEqual(len(connections['default'].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(connections['default'].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(connections['default'].queries), 2) def test_related_cache_is_full(self): reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) self.assertEqual(self.rsqs._cache_is_full(), False) results = self.rsqs.all() fire_the_iterator_and_fill_cache = [result for result in results] self.assertEqual(results._cache_is_full(), True) self.assertEqual(len(connections['default'].queries), 5) def test_quotes_regression(self): sqs = self.sqs.auto_query(u"44°48'40''N 20°28'32''E") # Should not have empty terms. self.assertEqual(sqs.query.build_query(), u"(44\xb048'40''N 20\xb028'32''E)") # Should not cause Solr to 500. self.assertEqual(sqs.count(), 0) sqs = self.sqs.auto_query('blazing') self.assertEqual(sqs.query.build_query(), u'(blazing)') self.assertEqual(sqs.count(), 0) sqs = self.sqs.auto_query('blazing saddles') self.assertEqual(sqs.query.build_query(), u'(blazing saddles)') self.assertEqual(sqs.count(), 0) sqs = self.sqs.auto_query('"blazing saddles') self.assertEqual(sqs.query.build_query(), u'(\\"blazing saddles)') self.assertEqual(sqs.count(), 0) sqs = self.sqs.auto_query('"blazing saddles"') self.assertEqual(sqs.query.build_query(), u'("blazing saddles")') self.assertEqual(sqs.count(), 0) sqs = self.sqs.auto_query('mel "blazing saddles"') self.assertEqual(sqs.query.build_query(), u'(mel "blazing saddles")') self.assertEqual(sqs.count(), 0) sqs = self.sqs.auto_query('mel "blazing \'saddles"') self.assertEqual(sqs.query.build_query(), u'(mel "blazing \'saddles")') self.assertEqual(sqs.count(), 0) sqs = self.sqs.auto_query('mel "blazing \'\'saddles"') self.assertEqual(sqs.query.build_query(), u'(mel "blazing \'\'saddles")') self.assertEqual(sqs.count(), 0) sqs = self.sqs.auto_query('mel "blazing \'\'saddles"\'') self.assertEqual(sqs.query.build_query(), u'(mel "blazing \'\'saddles" \')') self.assertEqual(sqs.count(), 0) sqs = self.sqs.auto_query('mel "blazing \'\'saddles"\'"') self.assertEqual(sqs.query.build_query(), u'(mel "blazing \'\'saddles" \'\\")') self.assertEqual(sqs.count(), 0) sqs = self.sqs.auto_query('"blazing saddles" mel') self.assertEqual(sqs.query.build_query(), u'("blazing saddles" mel)') self.assertEqual(sqs.count(), 0) sqs = self.sqs.auto_query('"blazing saddles" mel brooks') self.assertEqual(sqs.query.build_query(), u'("blazing saddles" mel brooks)') self.assertEqual(sqs.count(), 0) sqs = self.sqs.auto_query('mel "blazing saddles" brooks') self.assertEqual(sqs.query.build_query(), u'(mel "blazing saddles" brooks)') self.assertEqual(sqs.count(), 0) sqs = self.sqs.auto_query('mel "blazing saddles" "brooks') self.assertEqual(sqs.query.build_query(), u'(mel "blazing saddles" \\"brooks)') self.assertEqual(sqs.count(), 0) def test_query_generation(self): sqs = self.sqs.filter( SQ(content=AutoQuery("hello world")) | SQ(title=AutoQuery("hello world"))) self.assertEqual(sqs.query.build_query(), u"((hello world) OR title:(hello world))") 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 LiveSolrRelatedSearchQuerySetTestCase(TestCase): """Used to test actual implementation details of the RelatedSearchQuerySet.""" fixtures = ["solr_bulk_data.json"] def setUp(self): super(LiveSolrRelatedSearchQuerySetTestCase, 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.rsqs = RelatedSearchQuerySet() # 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(LiveSolrRelatedSearchQuerySetTestCase, self).tearDown() def test_load_all(self): sqs = self.rsqs.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.rsqs.load_all() self.assertEqual(len(sqs._load_all_querysets), 0) sqs = sqs.load_all_queryset(MockModel, MockModel.objects.filter(id__gt=1)) self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs._load_all_querysets), 1) self.assertEqual([obj.object.id for obj in sqs], range(2, 24)) def test_iter(self): backends.reset_search_queries() self.assertEqual(len(backends.queries), 0) sqs = self.rsqs.all() results = [int(result.pk) for result in sqs] self.assertEqual(results, range(1, 24)) self.assertEqual(len(backends.queries), 4) def test_slice(self): backends.reset_search_queries() self.assertEqual(len(backends.queries), 0) results = self.rsqs.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), 3) backends.reset_search_queries() self.assertEqual(len(backends.queries), 0) results = self.rsqs.all() self.assertEqual(int(results[21].pk), 22) self.assertEqual(len(backends.queries), 4) def test_manual_iter(self): results = self.rsqs.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), 4) def test_fill_cache(self): backends.reset_search_queries() self.assertEqual(len(backends.queries), 0) results = self.rsqs.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.rsqs._cache_is_full(), False) results = self.rsqs.all() fire_the_iterator_and_fill_cache = [result for result in results] self.assertEqual(results._cache_is_full(), True) self.assertEqual(len(backends.queries), 5)
class LiveSolrSearchQuerySetTestCase(TestCase): """Used to test actual implementation details of the SearchQuerySet.""" fixtures = ['bulk_data.json'] def setUp(self): super(LiveSolrSearchQuerySetTestCase, self).setUp() # Stow. self.old_debug = settings.DEBUG settings.DEBUG = True self.old_ui = connections['default'].get_unified_index() self.ui = UnifiedIndex() self.smmi = SolrMockSearchIndex() self.ui.build(indexes=[self.smmi]) connections['default']._index = self.ui self.sqs = SearchQuerySet() self.rsqs = RelatedSearchQuerySet() # Ugly but not constantly reindexing saves us almost 50% runtime. global lssqstc_all_loaded if lssqstc_all_loaded is None: print 'Reloading data...' lssqstc_all_loaded = True # Wipe it clean. clear_solr_index() # Force indexing of the content. self.smmi.update() def tearDown(self): # Restore. connections['default']._index = self.old_ui settings.DEBUG = self.old_debug super(LiveSolrSearchQuerySetTestCase, self).tearDown() def test_load_all(self): sqs = self.sqs.load_all() self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertTrue(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_iter(self): reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) sqs = self.sqs.all() results = [int(result.pk) for result in sqs] self.assertEqual(results, range(1, 24)) self.assertEqual(len(connections['default'].queries), 3) def test_slice(self): reset_search_queries() self.assertEqual(len(connections['default'].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(connections['default'].queries), 1) reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) results = self.sqs.all() self.assertEqual(int(results[21].pk), 22) self.assertEqual(len(connections['default'].queries), 1) def test_count(self): reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) sqs = self.sqs.all() self.assertEqual(sqs.count(), 23) self.assertEqual(sqs.count(), 23) self.assertEqual(len(sqs), 23) self.assertEqual(sqs.count(), 23) # Should only execute one query to count the length of the result set. self.assertEqual(len(connections['default'].queries), 1) def test_manual_iter(self): results = self.sqs.all() reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) results = [int(result.pk) for result in results._manual_iter()] self.assertEqual(results, range(1, 24)) self.assertEqual(len(connections['default'].queries), 3) def test_fill_cache(self): reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) results = self.sqs.all() self.assertEqual(len(results._result_cache), 0) self.assertEqual(len(connections['default'].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(connections['default'].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(connections['default'].queries), 2) def test_cache_is_full(self): reset_search_queries() self.assertEqual(len(connections['default'].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(connections['default'].queries), 3) def test___and__(self): sqs1 = self.sqs.filter(content='foo') sqs2 = self.sqs.filter(content='bar') sqs = sqs1 & sqs2 self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.query_filter), 2) self.assertEqual(sqs.query.build_query(), u'(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.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.query_filter), 3) self.assertEqual(sqs.query.build_query(), u'(NOT (title:moof) 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.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.query_filter), 2) self.assertEqual(sqs.query.build_query(), u'(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.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.query_filter), 2) self.assertEqual(sqs.query.build_query(), u'((NOT (title:moof) AND (foo OR baz)) OR bar)') def test_auto_query(self): # Ensure bits in exact matches get escaped properly as well. # This will break horrifically if escaping isn't working. sqs = self.sqs.auto_query('"pants:rule"') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(repr(sqs.query.query_filter), '<SQ: AND content__exact=pants\\:rule>') self.assertEqual(sqs.query.build_query(), u'"pants\\:rule"') self.assertEqual(len(sqs), 0) # Regressions def test_regression_proper_start_offsets(self): sqs = self.sqs.filter(text='index') self.assertNotEqual(sqs.count(), 0) id_counts = {} for item in sqs: if item.id in id_counts: id_counts[item.id] += 1 else: id_counts[item.id] = 1 for key, value in id_counts.items(): if value > 1: self.fail("Result with id '%s' seen more than once in the results." % key) def test_regression_raw_search_breaks_slicing(self): sqs = self.sqs.raw_search('text: index') page_1 = [result.pk for result in sqs[0:10]] page_2 = [result.pk for result in sqs[10:20]] for pk in page_2: if pk in page_1: self.fail("Result with id '%s' seen more than once in the results." % pk) # RelatedSearchQuerySet Tests def test_related_load_all(self): sqs = self.rsqs.load_all() self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertTrue(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_related_load_all_queryset(self): sqs = self.rsqs.load_all() self.assertEqual(len(sqs._load_all_querysets), 0) sqs = sqs.load_all_queryset(MockModel, MockModel.objects.filter(id__gt=1)) self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs._load_all_querysets), 1) self.assertEqual([obj.object.id for obj in sqs], range(2, 24)) sqs = sqs.load_all_queryset(MockModel, MockModel.objects.filter(id__gt=10)) self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs._load_all_querysets), 1) self.assertEqual([obj.object.id for obj in sqs], range(11, 24)) self.assertEqual([obj.object.id for obj in sqs[10:20]], [21, 22, 23]) def test_related_iter(self): reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) sqs = self.rsqs.all() results = [int(result.pk) for result in sqs] self.assertEqual(results, range(1, 24)) self.assertEqual(len(connections['default'].queries), 4) def test_related_slice(self): reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) results = self.rsqs.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(connections['default'].queries), 3) reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) results = self.rsqs.all() self.assertEqual(int(results[21].pk), 22) self.assertEqual(len(connections['default'].queries), 4) reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) results = self.rsqs.all() self.assertEqual([int(result.pk) for result in results[20:30]], [21, 22, 23]) self.assertEqual(len(connections['default'].queries), 4) def test_related_manual_iter(self): results = self.rsqs.all() reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) results = [int(result.pk) for result in results._manual_iter()] self.assertEqual(results, range(1, 24)) self.assertEqual(len(connections['default'].queries), 4) def test_related_fill_cache(self): reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) results = self.rsqs.all() self.assertEqual(len(results._result_cache), 0) self.assertEqual(len(connections['default'].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(connections['default'].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(connections['default'].queries), 2) def test_related_cache_is_full(self): reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) self.assertEqual(self.rsqs._cache_is_full(), False) results = self.rsqs.all() fire_the_iterator_and_fill_cache = [result for result in results] self.assertEqual(results._cache_is_full(), True) self.assertEqual(len(connections['default'].queries), 5) def test_quotes_regression(self): sqs = self.sqs.auto_query("44°48'40''N 20°28'32''E") # Should not have empty terms. self.assertEqual(sqs.query.build_query(), u"(44\ufffd\ufffd48'40''N AND 20\ufffd\ufffd28'32''E)") # Should not cause Solr to 500. self.assertEqual(sqs.count(), 0) sqs = self.sqs.auto_query('blazing') self.assertEqual(sqs.query.build_query(), u'blazing') self.assertEqual(sqs.count(), 0) sqs = self.sqs.auto_query('blazing saddles') self.assertEqual(sqs.query.build_query(), u'(blazing AND saddles)') self.assertEqual(sqs.count(), 0) sqs = self.sqs.auto_query('"blazing saddles') self.assertEqual(sqs.query.build_query(), u'(\\"blazing AND saddles)') self.assertEqual(sqs.count(), 0) sqs = self.sqs.auto_query('"blazing saddles"') self.assertEqual(sqs.query.build_query(), u'"blazing saddles"') self.assertEqual(sqs.count(), 0) sqs = self.sqs.auto_query('mel "blazing saddles"') self.assertEqual(sqs.query.build_query(), u'("blazing saddles" AND mel)') self.assertEqual(sqs.count(), 0) sqs = self.sqs.auto_query('mel "blazing \'saddles"') self.assertEqual(sqs.query.build_query(), u'("blazing \'saddles" AND mel)') self.assertEqual(sqs.count(), 0) sqs = self.sqs.auto_query('mel "blazing \'\'saddles"') self.assertEqual(sqs.query.build_query(), u'("blazing \'\'saddles" AND mel)') self.assertEqual(sqs.count(), 0) sqs = self.sqs.auto_query('mel "blazing \'\'saddles"\'') self.assertEqual(sqs.query.build_query(), u'("blazing \'\'saddles" AND mel AND \')') self.assertEqual(sqs.count(), 0) sqs = self.sqs.auto_query('mel "blazing \'\'saddles"\'"') self.assertEqual(sqs.query.build_query(), u'("blazing \'\'saddles" AND mel AND \'\\")') self.assertEqual(sqs.count(), 0) sqs = self.sqs.auto_query('"blazing saddles" mel') self.assertEqual(sqs.query.build_query(), u'("blazing saddles" AND mel)') self.assertEqual(sqs.count(), 0) sqs = self.sqs.auto_query('"blazing saddles" mel brooks') self.assertEqual(sqs.query.build_query(), u'("blazing saddles" AND mel AND brooks)') self.assertEqual(sqs.count(), 0) sqs = self.sqs.auto_query('mel "blazing saddles" brooks') self.assertEqual(sqs.query.build_query(), u'("blazing saddles" AND mel AND brooks)') self.assertEqual(sqs.count(), 0) sqs = self.sqs.auto_query('mel "blazing saddles" "brooks') self.assertEqual(sqs.query.build_query(), u'("blazing saddles" AND mel AND \\"brooks)') self.assertEqual(sqs.count(), 0) 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 LiveElasticsearchSearchQuerySetTestCase(TestCase): """Used to test actual implementation details of the SearchQuerySet.""" fixtures = ['bulk_data.json'] def setUp(self): super(LiveElasticsearchSearchQuerySetTestCase, self).setUp() # Stow. self.old_debug = settings.DEBUG settings.DEBUG = True self.old_ui = connections['default'].get_unified_index() self.ui = UnifiedIndex() self.smmi = ElasticsearchMockSearchIndex() self.ui.build(indexes=[self.smmi]) connections['default']._index = self.ui self.sqs = SearchQuerySet() self.rsqs = RelatedSearchQuerySet() # Ugly but not constantly reindexing saves us almost 50% runtime. global lssqstc_all_loaded if lssqstc_all_loaded is None: print 'Reloading data...' lssqstc_all_loaded = True # Wipe it clean. clear_elasticsearch_index() # Force indexing of the content. self.smmi.update() def tearDown(self): # Restore. connections['default']._index = self.old_ui settings.DEBUG = self.old_debug super(LiveElasticsearchSearchQuerySetTestCase, self).tearDown() def test_load_all(self): sqs = self.sqs.load_all() self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertTrue(len(sqs) > 0) self.assertEqual(sqs[0].object.foo, u'In addition, you may specify other fields to be populated along with the document. In this case, we also index the user who authored the document as well as the date the document was published. The variable you assign the SearchField to should directly map to the field your search backend is expecting. You instantiate most search fields with a parameter that points to the attribute of the object to populate that field with.') def test_iter(self): reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) sqs = self.sqs.all() results = sorted([int(result.pk) for result in sqs]) self.assertEqual(results, range(1, 24)) self.assertEqual(len(connections['default'].queries), 3) def test_slice(self): reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) results = self.sqs.all() self.assertEqual([int(result.pk) for result in results[1:11]], [7, 12, 17, 1, 6, 11, 16, 23, 5, 10]) self.assertEqual(len(connections['default'].queries), 1) reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) results = self.sqs.all() self.assertEqual(int(results[21].pk), 18) self.assertEqual(len(connections['default'].queries), 1) def test_count(self): reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) sqs = self.sqs.all() self.assertEqual(sqs.count(), 23) self.assertEqual(sqs.count(), 23) self.assertEqual(len(sqs), 23) self.assertEqual(sqs.count(), 23) # Should only execute one query to count the length of the result set. self.assertEqual(len(connections['default'].queries), 1) def test_manual_iter(self): results = self.sqs.all() reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) results = [int(result.pk) for result in results._manual_iter()] self.assertEqual(results, [2, 7, 12, 17, 1, 6, 11, 16, 23, 5, 10, 15, 22, 4, 9, 14, 19, 21, 3, 8, 13, 18, 20]) self.assertEqual(len(connections['default'].queries), 3) def test_fill_cache(self): reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) results = self.sqs.all() self.assertEqual(len(results._result_cache), 0) self.assertEqual(len(connections['default'].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(connections['default'].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(connections['default'].queries), 2) def test_cache_is_full(self): reset_search_queries() self.assertEqual(len(connections['default'].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(connections['default'].queries), 3) def test___and__(self): sqs1 = self.sqs.filter(content='foo') sqs2 = self.sqs.filter(content='bar') sqs = sqs1 & sqs2 self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.query_filter), 2) self.assertEqual(sqs.query.build_query(), u'(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.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.query_filter), 3) self.assertEqual(sqs.query.build_query(), u'(NOT (title:moof) 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.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.query_filter), 2) self.assertEqual(sqs.query.build_query(), u'(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.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.query_filter), 2) self.assertEqual(sqs.query.build_query(), u'((NOT (title:moof) AND (foo OR baz)) OR bar)') def test_auto_query(self): # Ensure bits in exact matches get escaped properly as well. # This will break horrifically if escaping isn't working. sqs = self.sqs.auto_query('"pants:rule"') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(repr(sqs.query.query_filter), '<SQ: AND content__contains="pants:rule">') self.assertEqual(sqs.query.build_query(), u'"pants\\:rule"') self.assertEqual(len(sqs), 0) # Regressions def test_regression_proper_start_offsets(self): sqs = self.sqs.filter(text='index') self.assertNotEqual(sqs.count(), 0) id_counts = {} for item in sqs: if item.id in id_counts: id_counts[item.id] += 1 else: id_counts[item.id] = 1 for key, value in id_counts.items(): if value > 1: self.fail("Result with id '%s' seen more than once in the results." % key) def test_regression_raw_search_breaks_slicing(self): sqs = self.sqs.raw_search('text:index') page_1 = [result.pk for result in sqs[0:10]] page_2 = [result.pk for result in sqs[10:20]] for pk in page_2: if pk in page_1: self.fail("Result with id '%s' seen more than once in the results." % pk) # RelatedSearchQuerySet Tests def test_related_load_all(self): sqs = self.rsqs.load_all() self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertTrue(len(sqs) > 0) self.assertEqual(sqs[0].object.foo, u'In addition, you may specify other fields to be populated along with the document. In this case, we also index the user who authored the document as well as the date the document was published. The variable you assign the SearchField to should directly map to the field your search backend is expecting. You instantiate most search fields with a parameter that points to the attribute of the object to populate that field with.') def test_related_load_all_queryset(self): sqs = self.rsqs.load_all() self.assertEqual(len(sqs._load_all_querysets), 0) sqs = sqs.load_all_queryset(MockModel, MockModel.objects.filter(id__gt=1)) self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs._load_all_querysets), 1) self.assertEqual(sorted([obj.object.id for obj in sqs]), range(2, 24)) sqs = sqs.load_all_queryset(MockModel, MockModel.objects.filter(id__gt=10)) self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs._load_all_querysets), 1) self.assertEqual([obj.object.id for obj in sqs], [12, 17, 11, 16, 23, 15, 22, 14, 19, 21, 13, 18, 20]) self.assertEqual([obj.object.id for obj in sqs[10:20]], [13, 18, 20]) def test_related_iter(self): reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) sqs = self.rsqs.all() results = [int(result.pk) for result in sqs] self.assertEqual(results, [2, 7, 12, 17, 1, 6, 11, 16, 23, 5, 10, 15, 22, 4, 9, 14, 19, 21, 3, 8, 13, 18, 20]) self.assertEqual(len(connections['default'].queries), 4) def test_related_slice(self): reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) results = self.rsqs.all() self.assertEqual([int(result.pk) for result in results[1:11]], [7, 12, 17, 1, 6, 11, 16, 23, 5, 10]) self.assertEqual(len(connections['default'].queries), 3) reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) results = self.rsqs.all() self.assertEqual(int(results[21].pk), 18) self.assertEqual(len(connections['default'].queries), 4) reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) results = self.rsqs.all() self.assertEqual([int(result.pk) for result in results[20:30]], [13, 18, 20]) self.assertEqual(len(connections['default'].queries), 4) def test_related_manual_iter(self): results = self.rsqs.all() reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) results = sorted([int(result.pk) for result in results._manual_iter()]) self.assertEqual(results, range(1, 24)) self.assertEqual(len(connections['default'].queries), 4) def test_related_fill_cache(self): reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) results = self.rsqs.all() self.assertEqual(len(results._result_cache), 0) self.assertEqual(len(connections['default'].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(connections['default'].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(connections['default'].queries), 2) def test_related_cache_is_full(self): reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) self.assertEqual(self.rsqs._cache_is_full(), False) results = self.rsqs.all() fire_the_iterator_and_fill_cache = [result for result in results] self.assertEqual(results._cache_is_full(), True) self.assertEqual(len(connections['default'].queries), 5) def test_quotes_regression(self): sqs = self.sqs.auto_query(u"44°48'40''N 20°28'32''E") # Should not have empty terms. self.assertEqual(sqs.query.build_query(), u"44\xb048'40''N 20\xb028'32''E") # Should not cause Elasticsearch to 500. self.assertEqual(sqs.count(), 0) sqs = self.sqs.auto_query('blazing') self.assertEqual(sqs.query.build_query(), u'blazing') self.assertEqual(sqs.count(), 0) sqs = self.sqs.auto_query('blazing saddles') self.assertEqual(sqs.query.build_query(), u'blazing saddles') self.assertEqual(sqs.count(), 0) sqs = self.sqs.auto_query('"blazing saddles') self.assertEqual(sqs.query.build_query(), u'\\"blazing saddles') self.assertEqual(sqs.count(), 0) sqs = self.sqs.auto_query('"blazing saddles"') self.assertEqual(sqs.query.build_query(), u'"blazing saddles"') self.assertEqual(sqs.count(), 0) sqs = self.sqs.auto_query('mel "blazing saddles"') self.assertEqual(sqs.query.build_query(), u'mel "blazing saddles"') self.assertEqual(sqs.count(), 0) sqs = self.sqs.auto_query('mel "blazing \'saddles"') self.assertEqual(sqs.query.build_query(), u'mel "blazing \'saddles"') self.assertEqual(sqs.count(), 0) sqs = self.sqs.auto_query('mel "blazing \'\'saddles"') self.assertEqual(sqs.query.build_query(), u'mel "blazing \'\'saddles"') self.assertEqual(sqs.count(), 0) sqs = self.sqs.auto_query('mel "blazing \'\'saddles"\'') self.assertEqual(sqs.query.build_query(), u'mel "blazing \'\'saddles" \'') self.assertEqual(sqs.count(), 0) sqs = self.sqs.auto_query('mel "blazing \'\'saddles"\'"') self.assertEqual(sqs.query.build_query(), u'mel "blazing \'\'saddles" \'\\"') self.assertEqual(sqs.count(), 0) sqs = self.sqs.auto_query('"blazing saddles" mel') self.assertEqual(sqs.query.build_query(), u'"blazing saddles" mel') self.assertEqual(sqs.count(), 0) sqs = self.sqs.auto_query('"blazing saddles" mel brooks') self.assertEqual(sqs.query.build_query(), u'"blazing saddles" mel brooks') self.assertEqual(sqs.count(), 0) sqs = self.sqs.auto_query('mel "blazing saddles" brooks') self.assertEqual(sqs.query.build_query(), u'mel "blazing saddles" brooks') self.assertEqual(sqs.count(), 0) sqs = self.sqs.auto_query('mel "blazing saddles" "brooks') self.assertEqual(sqs.query.build_query(), u'mel "blazing saddles" \\"brooks') self.assertEqual(sqs.count(), 0) 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 LiveElasticsearchSearchQuerySetTestCase(TestCase): """Used to test actual implementation details of the SearchQuerySet.""" fixtures = ['bulk_data.json'] def setUp(self): super(LiveElasticsearchSearchQuerySetTestCase, self).setUp() # Stow. self.old_debug = settings.DEBUG settings.DEBUG = True self.old_ui = connections['default'].get_unified_index() self.ui = UnifiedIndex() self.smmi = ElasticsearchMockSearchIndex() self.ui.build(indexes=[self.smmi]) connections['default']._index = self.ui self.sqs = SearchQuerySet() self.rsqs = RelatedSearchQuerySet() # Ugly but not constantly reindexing saves us almost 50% runtime. global lssqstc_all_loaded if lssqstc_all_loaded is None: lssqstc_all_loaded = True # Wipe it clean. clear_elasticsearch_index() # Force indexing of the content. self.smmi.update() def tearDown(self): # Restore. connections['default']._index = self.old_ui settings.DEBUG = self.old_debug super(LiveElasticsearchSearchQuerySetTestCase, self).tearDown() def test_load_all(self): sqs = self.sqs.load_all() self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertTrue(len(sqs) > 0) self.assertEqual(sqs[0].object.foo, u'In addition, you may specify other fields to be populated along with the document. In this case, we also index the user who authored the document as well as the date the document was published. The variable you assign the SearchField to should directly map to the field your search backend is expecting. You instantiate most search fields with a parameter that points to the attribute of the object to populate that field with.') def test_iter(self): reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) sqs = self.sqs.all() results = sorted([int(result.pk) for result in sqs]) self.assertEqual(results, range(1, 24)) self.assertEqual(len(connections['default'].queries), 3) def test_slice(self): reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) results = self.sqs.all() self.assertEqual([int(result.pk) for result in results[1:11]], [7, 12, 17, 1, 6, 11, 16, 23, 5, 10]) self.assertEqual(len(connections['default'].queries), 1) reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) results = self.sqs.all() self.assertEqual(int(results[21].pk), 18) self.assertEqual(len(connections['default'].queries), 1) def test_count(self): reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) sqs = self.sqs.all() self.assertEqual(sqs.count(), 23) self.assertEqual(sqs.count(), 23) self.assertEqual(len(sqs), 23) self.assertEqual(sqs.count(), 23) # Should only execute one query to count the length of the result set. self.assertEqual(len(connections['default'].queries), 1) def test_manual_iter(self): results = self.sqs.all() reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) results = [int(result.pk) for result in results._manual_iter()] self.assertEqual(results, [2, 7, 12, 17, 1, 6, 11, 16, 23, 5, 10, 15, 22, 4, 9, 14, 19, 21, 3, 8, 13, 18, 20]) self.assertEqual(len(connections['default'].queries), 3) def test_fill_cache(self): reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) results = self.sqs.all() self.assertEqual(len(results._result_cache), 0) self.assertEqual(len(connections['default'].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(connections['default'].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(connections['default'].queries), 2) def test_cache_is_full(self): reset_search_queries() self.assertEqual(len(connections['default'].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(connections['default'].queries), 3) def test___and__(self): sqs1 = self.sqs.filter(content='foo') sqs2 = self.sqs.filter(content='bar') sqs = sqs1 & sqs2 self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.query_filter), 2) self.assertEqual(sqs.query.build_query(), u'((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.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.query_filter), 3) self.assertEqual(sqs.query.build_query(), u'(NOT (title:(moof)) 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.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.query_filter), 2) self.assertEqual(sqs.query.build_query(), u'((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.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.query_filter), 2) self.assertEqual(sqs.query.build_query(), u'((NOT (title:(moof)) AND ((foo) OR (baz))) OR (bar))') def test_auto_query(self): # Ensure bits in exact matches get escaped properly as well. # This will break horrifically if escaping isn't working. sqs = self.sqs.auto_query('"pants:rule"') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(repr(sqs.query.query_filter), '<SQ: AND content__contains="pants:rule">') self.assertEqual(sqs.query.build_query(), u'("pants\\:rule")') self.assertEqual(len(sqs), 0) # Regressions @unittest.expectedFailure def test_regression_proper_start_offsets(self): sqs = self.sqs.filter(text='index') self.assertNotEqual(sqs.count(), 0) id_counts = {} for item in sqs: if item.id in id_counts: id_counts[item.id] += 1 else: id_counts[item.id] = 1 for key, value in id_counts.items(): if value > 1: self.fail("Result with id '%s' seen more than once in the results." % key) def test_regression_raw_search_breaks_slicing(self): sqs = self.sqs.raw_search('text:index') page_1 = [result.pk for result in sqs[0:10]] page_2 = [result.pk for result in sqs[10:20]] for pk in page_2: if pk in page_1: self.fail("Result with id '%s' seen more than once in the results." % pk) # RelatedSearchQuerySet Tests def test_related_load_all(self): sqs = self.rsqs.load_all() self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertTrue(len(sqs) > 0) self.assertEqual(sqs[0].object.foo, u'In addition, you may specify other fields to be populated along with the document. In this case, we also index the user who authored the document as well as the date the document was published. The variable you assign the SearchField to should directly map to the field your search backend is expecting. You instantiate most search fields with a parameter that points to the attribute of the object to populate that field with.') def test_related_load_all_queryset(self): sqs = self.rsqs.load_all() self.assertEqual(len(sqs._load_all_querysets), 0) sqs = sqs.load_all_queryset(MockModel, MockModel.objects.filter(id__gt=1)) self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs._load_all_querysets), 1) self.assertEqual(sorted([obj.object.id for obj in sqs]), range(2, 24)) sqs = sqs.load_all_queryset(MockModel, MockModel.objects.filter(id__gt=10)) self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs._load_all_querysets), 1) self.assertEqual([obj.object.id for obj in sqs], [12, 17, 11, 16, 23, 15, 22, 14, 19, 21, 13, 18, 20]) self.assertEqual([obj.object.id for obj in sqs[10:20]], [13, 18, 20]) def test_related_iter(self): reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) sqs = self.rsqs.all() results = [int(result.pk) for result in sqs] self.assertEqual(results, [2, 7, 12, 17, 1, 6, 11, 16, 23, 5, 10, 15, 22, 4, 9, 14, 19, 21, 3, 8, 13, 18, 20]) self.assertEqual(len(connections['default'].queries), 4) def test_related_slice(self): reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) results = self.rsqs.all() self.assertEqual([int(result.pk) for result in results[1:11]], [7, 12, 17, 1, 6, 11, 16, 23, 5, 10]) self.assertEqual(len(connections['default'].queries), 3) reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) results = self.rsqs.all() self.assertEqual(int(results[21].pk), 18) self.assertEqual(len(connections['default'].queries), 4) reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) results = self.rsqs.all() self.assertEqual([int(result.pk) for result in results[20:30]], [13, 18, 20]) self.assertEqual(len(connections['default'].queries), 4) def test_related_manual_iter(self): results = self.rsqs.all() reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) results = sorted([int(result.pk) for result in results._manual_iter()]) self.assertEqual(results, range(1, 24)) self.assertEqual(len(connections['default'].queries), 4) def test_related_fill_cache(self): reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) results = self.rsqs.all() self.assertEqual(len(results._result_cache), 0) self.assertEqual(len(connections['default'].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(connections['default'].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(connections['default'].queries), 2) def test_related_cache_is_full(self): reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) self.assertEqual(self.rsqs._cache_is_full(), False) results = self.rsqs.all() fire_the_iterator_and_fill_cache = [result for result in results] self.assertEqual(results._cache_is_full(), True) self.assertEqual(len(connections['default'].queries), 5) def test_quotes_regression(self): sqs = self.sqs.auto_query(u"44°48'40''N 20°28'32''E") # Should not have empty terms. self.assertEqual(sqs.query.build_query(), u"(44\xb048'40''N 20\xb028'32''E)") # Should not cause Elasticsearch to 500. self.assertEqual(sqs.count(), 0) sqs = self.sqs.auto_query('blazing') self.assertEqual(sqs.query.build_query(), u'(blazing)') self.assertEqual(sqs.count(), 0) sqs = self.sqs.auto_query('blazing saddles') self.assertEqual(sqs.query.build_query(), u'(blazing saddles)') self.assertEqual(sqs.count(), 0) sqs = self.sqs.auto_query('"blazing saddles') self.assertEqual(sqs.query.build_query(), u'(\\"blazing saddles)') self.assertEqual(sqs.count(), 0) sqs = self.sqs.auto_query('"blazing saddles"') self.assertEqual(sqs.query.build_query(), u'("blazing saddles")') self.assertEqual(sqs.count(), 0) sqs = self.sqs.auto_query('mel "blazing saddles"') self.assertEqual(sqs.query.build_query(), u'(mel "blazing saddles")') self.assertEqual(sqs.count(), 0) sqs = self.sqs.auto_query('mel "blazing \'saddles"') self.assertEqual(sqs.query.build_query(), u'(mel "blazing \'saddles")') self.assertEqual(sqs.count(), 0) sqs = self.sqs.auto_query('mel "blazing \'\'saddles"') self.assertEqual(sqs.query.build_query(), u'(mel "blazing \'\'saddles")') self.assertEqual(sqs.count(), 0) sqs = self.sqs.auto_query('mel "blazing \'\'saddles"\'') self.assertEqual(sqs.query.build_query(), u'(mel "blazing \'\'saddles" \')') self.assertEqual(sqs.count(), 0) sqs = self.sqs.auto_query('mel "blazing \'\'saddles"\'"') self.assertEqual(sqs.query.build_query(), u'(mel "blazing \'\'saddles" \'\\")') self.assertEqual(sqs.count(), 0) sqs = self.sqs.auto_query('"blazing saddles" mel') self.assertEqual(sqs.query.build_query(), u'("blazing saddles" mel)') self.assertEqual(sqs.count(), 0) sqs = self.sqs.auto_query('"blazing saddles" mel brooks') self.assertEqual(sqs.query.build_query(), u'("blazing saddles" mel brooks)') self.assertEqual(sqs.count(), 0) sqs = self.sqs.auto_query('mel "blazing saddles" brooks') self.assertEqual(sqs.query.build_query(), u'(mel "blazing saddles" brooks)') self.assertEqual(sqs.count(), 0) sqs = self.sqs.auto_query('mel "blazing saddles" "brooks') self.assertEqual(sqs.query.build_query(), u'(mel "blazing saddles" \\"brooks)') self.assertEqual(sqs.count(), 0) def test_query_generation(self): sqs = self.sqs.filter(SQ(content=AutoQuery("hello world")) | SQ(title=AutoQuery("hello world"))) self.assertEqual(sqs.query.build_query(), u"((hello world) OR title:(hello world))") 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 LiveSolrSearchQuerySetTestCase(TestCase): """Used to test actual implementation details of the SearchQuerySet.""" fixtures = ['bulk_data.json'] def setUp(self): super(LiveSolrSearchQuerySetTestCase, 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, SolrMockModelSearchIndex) haystack.site = test_site self.sqs = SearchQuerySet() self.rsqs = RelatedSearchQuerySet() # Ugly but not constantly reindexing saves us almost 50% runtime. global lssqstc_all_loaded if lssqstc_all_loaded is None: print 'Reloading data...' lssqstc_all_loaded = True # Wipe it clean. clear_solr_index() # Force indexing of the content. mockmodel_index = test_site.get_index(MockModel) mockmodel_index.update() def tearDown(self): # Restore. import haystack haystack.site = self.old_site settings.DEBUG = self.old_debug super(LiveSolrSearchQuerySetTestCase, 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_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(), u'(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(), u'(NOT (title:moof) 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(), u'(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(), u'((NOT (title:moof) AND (foo OR baz)) OR bar)') def test_auto_query(self): # Ensure bits in exact matches get escaped properly as well. # This will break horrifically if escaping isn't working. sqs = self.sqs.auto_query('"pants:rule"') self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual(repr(sqs.query.query_filter), '<SQ: AND content__exact=pants\\:rule>') self.assertEqual(sqs.query.build_query(), u'pants\\:rule') self.assertEqual(len(sqs), 0) # Regressions def test_regression_proper_start_offsets(self): sqs = self.sqs.filter(text='index') self.assertNotEqual(sqs.count(), 0) id_counts = {} for item in sqs: if item.id in id_counts: id_counts[item.id] += 1 else: id_counts[item.id] = 1 for key, value in id_counts.items(): if value > 1: self.fail("Result with id '%s' seen more than once in the results." % key) def test_regression_raw_search_breaks_slicing(self): sqs = self.sqs.raw_search('text: index') page_1 = [result.pk for result in sqs[0:10]] page_2 = [result.pk for result in sqs[10:20]] for pk in page_2: if pk in page_1: self.fail("Result with id '%s' seen more than once in the results." % pk) # RelatedSearchQuerySet Tests def test_related_load_all(self): sqs = self.rsqs.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_related_load_all_queryset(self): sqs = self.rsqs.load_all() self.assertEqual(len(sqs._load_all_querysets), 0) sqs = sqs.load_all_queryset(MockModel, MockModel.objects.filter(id__gt=1)) self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs._load_all_querysets), 1) self.assertEqual([obj.object.id for obj in sqs], range(2, 24)) sqs = sqs.load_all_queryset(MockModel, MockModel.objects.filter(id__gt=10)) self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs._load_all_querysets), 1) self.assertEqual([obj.object.id for obj in sqs], range(11, 24)) self.assertEqual([obj.object.id for obj in sqs[10:20]], [21, 22, 23]) def test_related_iter(self): backends.reset_search_queries() self.assertEqual(len(backends.queries), 0) sqs = self.rsqs.all() results = [int(result.pk) for result in sqs] self.assertEqual(results, range(1, 24)) self.assertEqual(len(backends.queries), 4) def test_related_slice(self): backends.reset_search_queries() self.assertEqual(len(backends.queries), 0) results = self.rsqs.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), 3) backends.reset_search_queries() self.assertEqual(len(backends.queries), 0) results = self.rsqs.all() self.assertEqual(int(results[21].pk), 22) self.assertEqual(len(backends.queries), 4) backends.reset_search_queries() self.assertEqual(len(backends.queries), 0) results = self.rsqs.all() self.assertEqual([int(result.pk) for result in results[20:30]], [21, 22, 23]) self.assertEqual(len(backends.queries), 4) def test_related_manual_iter(self): results = self.rsqs.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), 4) def test_related_fill_cache(self): backends.reset_search_queries() self.assertEqual(len(backends.queries), 0) results = self.rsqs.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_related_cache_is_full(self): backends.reset_search_queries() self.assertEqual(len(backends.queries), 0) self.assertEqual(self.rsqs._cache_is_full(), False) results = self.rsqs.all() fire_the_iterator_and_fill_cache = [result for result in results] self.assertEqual(results._cache_is_full(), True) self.assertEqual(len(backends.queries), 5)
class LiveSolrSearchQuerySetTestCase(TestCase): """Used to test actual implementation details of the SearchQuerySet.""" fixtures = ['bulk_data.json'] def setUp(self): super(LiveSolrSearchQuerySetTestCase, 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, SolrMockModelSearchIndex) haystack.site = test_site self.sqs = SearchQuerySet() self.rsqs = RelatedSearchQuerySet() # Ugly but not constantly reindexing saves us almost 50% runtime. global lssqstc_all_loaded if lssqstc_all_loaded is None: print 'Reloading data...' lssqstc_all_loaded = True # Wipe it clean. clear_solr_index() # Force indexing of the content. mockmodel_index = test_site.get_index(MockModel) mockmodel_index.update() def tearDown(self): # Restore. import haystack haystack.site = self.old_site settings.DEBUG = self.old_debug super(LiveSolrSearchQuerySetTestCase, 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_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_count(self): backends.reset_search_queries() self.assertEqual(len(backends.queries), 0) sqs = self.sqs.all() self.assertEqual(sqs.count(), 23) self.assertEqual(sqs.count(), 23) self.assertEqual(len(sqs), 23) self.assertEqual(sqs.count(), 23) # Should only execute one query to count the length of the result set. 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(), u'(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(), u'(NOT (title:moof) 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(), u'(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(), u'((NOT (title:moof) AND (foo OR baz)) OR bar)') def test_auto_query(self): # Ensure bits in exact matches get escaped properly as well. # This will break horrifically if escaping isn't working. sqs = self.sqs.auto_query('"pants:rule"') self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual(repr(sqs.query.query_filter), '<SQ: AND content__exact=pants\\:rule>') self.assertEqual(sqs.query.build_query(), u'pants\\:rule') self.assertEqual(len(sqs), 0) # Regressions def test_regression_proper_start_offsets(self): sqs = self.sqs.filter(text='index') self.assertNotEqual(sqs.count(), 0) id_counts = {} for item in sqs: if item.id in id_counts: id_counts[item.id] += 1 else: id_counts[item.id] = 1 for key, value in id_counts.items(): if value > 1: self.fail( "Result with id '%s' seen more than once in the results." % key) def test_regression_raw_search_breaks_slicing(self): sqs = self.sqs.raw_search('text: index') page_1 = [result.pk for result in sqs[0:10]] page_2 = [result.pk for result in sqs[10:20]] for pk in page_2: if pk in page_1: self.fail( "Result with id '%s' seen more than once in the results." % pk) # RelatedSearchQuerySet Tests def test_related_load_all(self): sqs = self.rsqs.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_related_load_all_queryset(self): sqs = self.rsqs.load_all() self.assertEqual(len(sqs._load_all_querysets), 0) sqs = sqs.load_all_queryset(MockModel, MockModel.objects.filter(id__gt=1)) self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs._load_all_querysets), 1) self.assertEqual([obj.object.id for obj in sqs], range(2, 24)) sqs = sqs.load_all_queryset(MockModel, MockModel.objects.filter(id__gt=10)) self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs._load_all_querysets), 1) self.assertEqual([obj.object.id for obj in sqs], range(11, 24)) self.assertEqual([obj.object.id for obj in sqs[10:20]], [21, 22, 23]) def test_related_iter(self): backends.reset_search_queries() self.assertEqual(len(backends.queries), 0) sqs = self.rsqs.all() results = [int(result.pk) for result in sqs] self.assertEqual(results, range(1, 24)) self.assertEqual(len(backends.queries), 4) def test_related_slice(self): backends.reset_search_queries() self.assertEqual(len(backends.queries), 0) results = self.rsqs.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), 3) backends.reset_search_queries() self.assertEqual(len(backends.queries), 0) results = self.rsqs.all() self.assertEqual(int(results[21].pk), 22) self.assertEqual(len(backends.queries), 4) backends.reset_search_queries() self.assertEqual(len(backends.queries), 0) results = self.rsqs.all() self.assertEqual([int(result.pk) for result in results[20:30]], [21, 22, 23]) self.assertEqual(len(backends.queries), 4) def test_related_manual_iter(self): results = self.rsqs.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), 4) def test_related_fill_cache(self): backends.reset_search_queries() self.assertEqual(len(backends.queries), 0) results = self.rsqs.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_related_cache_is_full(self): backends.reset_search_queries() self.assertEqual(len(backends.queries), 0) self.assertEqual(self.rsqs._cache_is_full(), False) results = self.rsqs.all() fire_the_iterator_and_fill_cache = [result for result in results] self.assertEqual(results._cache_is_full(), True) self.assertEqual(len(backends.queries), 5)