def handle(self, *args, **options): if len(args) != 0: raise CommandError("No parameters are required for this command") tutorials = Tutorial.objects.all() sqs = SearchQuerySet() # loop over each tutorial for tutorial in tutorials: print 'Processing %s' % tutorial.title related_tuts = sqs.more_like_this(tutorial).all() num_related = related_tuts.count() print ' %s related tutorials' % num_related for index in xrange(num_related): if index > 20: # Only store the top 20 break rel = related_tuts[index] if not rel or not rel.object: import pdb; pdb.set_trace() break tutorial.related.add(rel.object) tutorial.save()
def render(self, context): try: model_instance = self.model.resolve(context) sqs = SearchQuerySet() if self.for_types is not None: intermediate = template.Variable(self.for_types) for_types = intermediate.resolve(context).split(",") search_models = [] for model in for_types: model_class = haystack_get_model(*model.split(".")) if model_class: search_models.append(model_class) sqs = sqs.models(*search_models) sqs = sqs.more_like_this(model_instance) if self.limit is not None: sqs = sqs[: self.limit] context[self.varname] = sqs except Exception as exc: logging.warning( "Unhandled exception rendering %r: %s", self, exc, exc_info=True ) return ""
def render(self, context): try: model_instance = self.model.resolve(context) sqs = SearchQuerySet() if not self.for_types is None: intermediate = template.Variable(self.for_types) for_types = intermediate.resolve(context).split(',') search_models = [] for model in for_types: model_class = models.get_model(*model.split('.')) if model_class: search_models.append(model_class) sqs = sqs.models(*search_models) sqs = sqs.more_like_this(model_instance) if not self.limit is None: sqs = sqs[:self.limit] context[self.varname] = sqs except: pass return ''
def render(self, context): try: model_instance = self.model.resolve(context) sqs = SearchQuerySet() if not self.for_types is None: intermediate = template.Variable(self.for_types) for_types = intermediate.resolve(context).split(',') search_models = [] for model in for_types: model_class = haystack_get_model(*model.split('.')) if model_class: search_models.append(model_class) sqs = sqs.models(*search_models) sqs = sqs.more_like_this(model_instance) if not self.limit is None: sqs = sqs[:self.limit] context[self.varname] = sqs except: pass return ''
def handle(self, *args, **options): if len(args) != 0: raise CommandError("No parameters are required for this command") tutorials = Tutorial.objects.all() sqs = SearchQuerySet() # loop over each tutorial for tutorial in tutorials: print 'Processing %s' % tutorial.title related_tuts = sqs.more_like_this(tutorial).all() num_related = related_tuts.count() print ' %s related tutorials' % num_related index = 0 for rel in [x for x in related_tuts.all() if type(x.object) == Tutorial]: if index > 20: # Only store the top 20 break if not rel or not rel.object: import pdb; pdb.set_trace() break tutorial.related.add(rel.object) tutorial.save() index = index + 1
def render(self, context): try: model_instance = self.model.resolve(context) sqs = SearchQuerySet() if self.for_types is not None: intermediate = template.Variable(self.for_types) for_types = intermediate.resolve(context).split(",") search_models = [] for model in for_types: model_class = haystack_get_model(*model.split(".")) if model_class: search_models.append(model_class) sqs = sqs.models(*search_models) sqs = sqs.more_like_this(model_instance) if self.limit is not None: sqs = sqs[:self.limit] context[self.varname] = sqs except Exception as exc: logging.warning("Unhandled exception rendering %r: %s", self, exc, exc_info=True) return ""
class LiveSolrMoreLikeThisTestCase(TestCase): fixtures = ['bulk_data.json'] def setUp(self): super(LiveSolrMoreLikeThisTestCase, self).setUp() # Wipe it clean. clear_solr_index() # With the models registered, you get the proper bits. import haystack from haystack.sites import SearchSite # Stow. self.old_site = haystack.site test_site = SearchSite() test_site.register(MockModel, SolrMockModelSearchIndex) test_site.register(AnotherMockModel, SolrAnotherMockModelSearchIndex) haystack.site = test_site self.sqs = SearchQuerySet() test_site.get_index(MockModel).update() test_site.get_index(AnotherMockModel).update() def tearDown(self): # Restore. import haystack haystack.site = self.old_site super(LiveSolrMoreLikeThisTestCase, self).tearDown() def test_more_like_this(self): mlt = self.sqs.more_like_this(MockModel.objects.get(pk=1)) self.assertEqual(mlt.count(), 24) self.assertEqual([result.pk for result in mlt], ['6', '14', '4', '10', '22', '5', '3', '12', '2', '23', '18', '19', '13', '7', '15', '21', '9', '1', '2', '20', '16', '17', '8', '11']) self.assertEqual(len([result.pk for result in mlt]), 24) alt_mlt = self.sqs.filter(name='daniel3').more_like_this(MockModel.objects.get(pk=3)) self.assertEqual(alt_mlt.count(), 10) self.assertEqual([result.pk for result in alt_mlt], ['23', '13', '17', '16', '22', '19', '4', '10', '1', '2']) self.assertEqual(len([result.pk for result in alt_mlt]), 10) alt_mlt_with_models = self.sqs.models(MockModel).more_like_this(MockModel.objects.get(pk=1)) self.assertEqual(alt_mlt_with_models.count(), 22) self.assertEqual([result.pk for result in alt_mlt_with_models], ['6', '14', '4', '10', '22', '5', '3', '12', '2', '23', '18', '19', '13', '7', '15', '21', '9', '20', '16', '17', '8', '11']) self.assertEqual(len([result.pk for result in alt_mlt_with_models]), 22) if hasattr(MockModel.objects, 'defer'): # Make sure MLT works with deferred bits. mi = MockModel.objects.defer('foo').get(pk=1) self.assertEqual(mi._deferred, True) deferred = self.sqs.models(MockModel).more_like_this(mi) self.assertEqual(deferred.count(), 0) self.assertEqual([result.pk for result in deferred], []) self.assertEqual(len([result.pk for result in deferred]), 0) # Ensure that swapping the ``result_class`` works. self.assertTrue(isinstance(self.sqs.result_class(MockSearchResult).more_like_this(MockModel.objects.get(pk=1))[0], MockSearchResult))
class LiveWhooshMoreLikeThisTestCase(WhooshTestCase): fixtures = ['bulk_data.json'] def setUp(self): super(LiveWhooshMoreLikeThisTestCase, self).setUp() # Stow. self.old_ui = connections['whoosh'].get_unified_index() self.ui = UnifiedIndex() self.wmmi = WhooshMockSearchIndex() self.wamsi = WhooshAnotherMockSearchIndex() self.ui.build(indexes=[self.wmmi, self.wamsi]) self.sb = connections['whoosh'].get_backend() connections['whoosh']._index = self.ui self.sb.setup() self.raw_whoosh = self.sb.index self.parser = QueryParser(self.sb.content_field_name, schema=self.sb.schema) self.sb.delete_index() self.wmmi.update() self.wamsi.update() self.sqs = SearchQuerySet('whoosh') def tearDown(self): connections['whoosh']._index = self.old_ui super(LiveWhooshMoreLikeThisTestCase, self).tearDown() # We expect failure here because, despite not changing the code, Whoosh # 2.5.1 returns incorrect counts/results. Huzzah. @unittest.expectedFailure def test_more_like_this(self): mlt = self.sqs.more_like_this(MockModel.objects.get(pk=22)) self.assertEqual(mlt.count(), 22) self.assertEqual(sorted([result.pk for result in mlt]), sorted([u'9', u'8', u'7', u'6', u'5', u'4', u'3', u'2', u'1', u'21', u'20', u'19', u'18', u'17', u'16', u'15', u'14', u'13', u'12', u'11', u'10', u'23'])) self.assertEqual(len([result.pk for result in mlt]), 22) alt_mlt = self.sqs.filter(name='daniel3').more_like_this(MockModel.objects.get(pk=13)) self.assertEqual(alt_mlt.count(), 8) self.assertEqual(sorted([result.pk for result in alt_mlt]), sorted([u'4', u'3', u'22', u'19', u'17', u'16', u'10', u'23'])) self.assertEqual(len([result.pk for result in alt_mlt]), 8) alt_mlt_with_models = self.sqs.models(MockModel).more_like_this(MockModel.objects.get(pk=11)) self.assertEqual(alt_mlt_with_models.count(), 22) self.assertEqual(sorted([result.pk for result in alt_mlt_with_models]), sorted([u'9', u'8', u'7', u'6', u'5', u'4', u'3', u'2', u'1', u'22', u'21', u'20', u'19', u'18', u'17', u'16', u'15', u'14', u'13', u'12', u'10', u'23'])) self.assertEqual(len([result.pk for result in alt_mlt_with_models]), 22) if hasattr(MockModel.objects, 'defer'): # Make sure MLT works with deferred bits. mi = MockModel.objects.defer('foo').get(pk=21) self.assertEqual(mi._deferred, True) deferred = self.sqs.models(MockModel).more_like_this(mi) self.assertEqual(deferred.count(), 0) self.assertEqual([result.pk for result in deferred], []) self.assertEqual(len([result.pk for result in deferred]), 0) # Ensure that swapping the ``result_class`` works. self.assertTrue(isinstance(self.sqs.result_class(MockSearchResult).more_like_this(MockModel.objects.get(pk=21))[0], MockSearchResult))
class LiveSolrMoreLikeThisTestCase(TestCase): fixtures = ['bulk_data.json'] def setUp(self): super(LiveSolrMoreLikeThisTestCase, self).setUp() # Wipe it clean. clear_solr_index() # With the models registered, you get the proper bits. import haystack from haystack.sites import SearchSite # Stow. self.old_site = haystack.site test_site = SearchSite() test_site.register(MockModel, SolrMockModelSearchIndex) test_site.register(AnotherMockModel, SolrAnotherMockModelSearchIndex) haystack.site = test_site self.sqs = SearchQuerySet() # Force indexing of the content. for mock in MockModel.objects.all(): mock.save() # Force indexing of the content. for mock in AnotherMockModel.objects.all(): mock.save() def tearDown(self): # Restore. import haystack haystack.site = self.old_site super(LiveSolrMoreLikeThisTestCase, self).tearDown() def test_more_like_this(self): mlt = self.sqs.more_like_this(MockModel.objects.get(pk=1)) self.assertEqual(mlt.count(), 25) self.assertEqual([result.pk for result in mlt], ['6', '14', '4', '10', '22', '5', '3', '12', '2', '23', '18', '19', '13', '7', '15', '21', '9', '1', '2', '20', '16', '17', '8', '11']) alt_mlt = self.sqs.filter(name='daniel3').more_like_this(MockModel.objects.get(pk=3)) self.assertEqual(alt_mlt.count(), 11) self.assertEqual([result.pk for result in alt_mlt], ['23', '13', '17', '16', '22', '19', '4', '10', '1', '2']) alt_mlt_with_models = self.sqs.models(MockModel).more_like_this(MockModel.objects.get(pk=1)) self.assertEqual(alt_mlt_with_models.count(), 23) self.assertEqual([result.pk for result in alt_mlt_with_models], ['6', '14', '4', '10', '22', '5', '3', '12', '2', '23', '18', '19', '13', '7', '15', '21', '9', '20', '16', '17', '8', '11']) if hasattr(MockModel.objects, 'defer'): # Make sure MLT works with deferred bits. mi = MockModel.objects.defer('foo').get(pk=1) self.assertEqual(mi._deferred, True) deferred = self.sqs.models(MockModel).more_like_this(mi) self.assertEqual(alt_mlt_with_models.count(), 23) self.assertEqual([result.pk for result in alt_mlt_with_models], ['6', '14', '4', '10', '22', '5', '3', '12', '2', '23', '18', '19', '13', '7', '15', '21', '9', '20', '16', '17', '8', '11'])
class LiveElasticsearchMoreLikeThisTestCase(TestCase): fixtures = ['bulk_data.json'] def setUp(self): super(LiveElasticsearchMoreLikeThisTestCase, self).setUp() # Wipe it clean. clear_elasticsearch_index() self.old_ui = connections['default'].get_unified_index() self.ui = UnifiedIndex() self.smmi = ElasticsearchMockModelSearchIndex() self.sammi = ElasticsearchAnotherMockModelSearchIndex() self.ui.build(indexes=[self.smmi, self.sammi]) connections['default']._index = self.ui self.sqs = SearchQuerySet() self.smmi.update() self.sammi.update() def tearDown(self): # Restore. connections['default']._index = self.old_ui super(LiveElasticsearchMoreLikeThisTestCase, self).tearDown() @unittest.expectedFailure def test_more_like_this(self): mlt = self.sqs.more_like_this(MockModel.objects.get(pk=1)) self.assertEqual(mlt.count(), 4) self.assertEqual([result.pk for result in mlt], [u'2', u'6', u'16', u'23']) self.assertEqual(len([result.pk for result in mlt]), 4) alt_mlt = self.sqs.filter(name='daniel3').more_like_this(MockModel.objects.get(pk=2)) self.assertEqual(alt_mlt.count(), 6) self.assertEqual([result.pk for result in alt_mlt], [u'2', u'6', u'16', u'23', u'1', u'11']) self.assertEqual(len([result.pk for result in alt_mlt]), 6) alt_mlt_with_models = self.sqs.models(MockModel).more_like_this(MockModel.objects.get(pk=1)) self.assertEqual(alt_mlt_with_models.count(), 4) self.assertEqual([result.pk for result in alt_mlt_with_models], [u'2', u'6', u'16', u'23']) self.assertEqual(len([result.pk for result in alt_mlt_with_models]), 4) if hasattr(MockModel.objects, 'defer'): # Make sure MLT works with deferred bits. mi = MockModel.objects.defer('foo').get(pk=1) self.assertEqual(mi._deferred, True) deferred = self.sqs.models(MockModel).more_like_this(mi) self.assertEqual(deferred.count(), 0) self.assertEqual([result.pk for result in deferred], []) self.assertEqual(len([result.pk for result in deferred]), 0) # Ensure that swapping the ``result_class`` works. self.assertTrue(isinstance(self.sqs.result_class(MockSearchResult).more_like_this(MockModel.objects.get(pk=1))[0], MockSearchResult))
class LiveElasticsearchMoreLikeThisTestCase(TestCase): fixtures = ['bulk_data.json'] def setUp(self): super(LiveElasticsearchMoreLikeThisTestCase, self).setUp() # Wipe it clean. clear_elasticsearch_index() self.old_ui = connections['default'].get_unified_index() self.ui = UnifiedIndex() self.smmi = ElasticsearchMockModelSearchIndex() self.sammi = ElasticsearchAnotherMockModelSearchIndex() self.ui.build(indexes=[self.smmi, self.sammi]) connections['default']._index = self.ui self.sqs = SearchQuerySet() self.smmi.update() self.sammi.update() def tearDown(self): # Restore. connections['default']._index = self.old_ui super(LiveElasticsearchMoreLikeThisTestCase, self).tearDown() @unittest.expectedFailure def test_more_like_this(self): mlt = self.sqs.more_like_this(MockModel.objects.get(pk=1)) self.assertEqual(mlt.count(), 4) self.assertEqual([result.pk for result in mlt], [u'2', u'6', u'16', u'23']) self.assertEqual(len([result.pk for result in mlt]), 4) alt_mlt = self.sqs.filter(name='daniel3').more_like_this(MockModel.objects.get(pk=2)) self.assertEqual(alt_mlt.count(), 6) self.assertEqual([result.pk for result in alt_mlt], [u'2', u'6', u'16', u'23', u'1', u'11']) self.assertEqual(len([result.pk for result in alt_mlt]), 6) alt_mlt_with_models = self.sqs.models(MockModel).more_like_this(MockModel.objects.get(pk=1)) self.assertEqual(alt_mlt_with_models.count(), 4) self.assertEqual([result.pk for result in alt_mlt_with_models], [u'2', u'6', u'16', u'23']) self.assertEqual(len([result.pk for result in alt_mlt_with_models]), 4) if hasattr(MockModel.objects, 'defer'): # Make sure MLT works with deferred bits. mi = MockModel.objects.defer('foo').get(pk=1) self.assertEqual(mi._deferred, True) deferred = self.sqs.models(MockModel).more_like_this(mi) self.assertEqual(deferred.count(), 0) self.assertEqual([result.pk for result in deferred], []) self.assertEqual(len([result.pk for result in deferred]), 0) # Ensure that swapping the ``result_class`` works. self.assertTrue(isinstance(self.sqs.result_class(MockSearchResult).more_like_this(MockModel.objects.get(pk=1))[0], MockSearchResult))
def handle_item(self, item): """Perform MLT-query on item""" sqs = SearchQuerySet().models(self.model) mlt_results = sqs.more_like_this(item) saved = [] for result in mlt_results: # type: SearchResult rel = self.model_relation(score=result.score) rel.set_relation(seed_id=item.pk, related_id=result.pk) rel.save() logger.debug('Saved %s' % rel) saved.append(rel) return saved
def render_into_region(self, obj, context, **kwargs): sqs = SearchQuerySet().using(obj.connection) if obj.request_objects: sqs = sqs.load_all() try: context.update({ 'more_like_this': sqs.more_like_this( obj.content_object)[0:obj.max_num] }) except NotHandled: logger.exception("Haystack hasn't been configured to handle this " "object type.") context.update({'more_like_this': ()}) except SearchBackendError: logger.exception("Recovering from search backend error by failing " "silently.") context.update({'more_like_this': ()}) return render_to_string('editregions/search/mlt.html', context_instance=context)
class SearchQuerySetTestCase(TestCase): def setUp(self): super(SearchQuerySetTestCase, self).setUp() self.bsqs = SearchQuerySet(query=DummySearchQuery(backend=DummySearchBackend())) self.msqs = SearchQuerySet(query=MockSearchQuery(backend=MockSearchBackend())) self.mmsqs = SearchQuerySet(query=MockSearchQuery(backend=MixedMockSearchBackend())) # Stow. self.old_site = haystack.site test_site = SearchSite() test_site.register(MockModel) haystack.site = test_site def tearDown(self): # Restore. haystack.site = self.old_site super(SearchQuerySetTestCase, self).tearDown() def test_len(self): # Dummy always returns 0. self.assertEqual(len(self.bsqs), 0) self.assertEqual(len(self.msqs), 100) def test_iter(self): # Dummy always returns []. self.assertEqual([result for result in self.bsqs.all()], []) msqs = self.msqs.all() results = [result for result in msqs] self.assertEqual(results, MOCK_SEARCH_RESULTS) def test_slice(self): self.assertEqual(self.msqs.all()[1:11], MOCK_SEARCH_RESULTS[1:11]) self.assertEqual(self.msqs.all()[50], MOCK_SEARCH_RESULTS[50]) def test_manual_iter(self): results = self.msqs.all() for offset, result in enumerate(results._manual_iter()): self.assertEqual(result, MOCK_SEARCH_RESULTS[offset]) # Test to ensure we properly fill the cache, even if we get fewer # results back (not in the SearchSite) than the hit count indicates. # This will hang indefinitely if broken. results = self.mmsqs.all() self.assertEqual([result.pk for result in results._manual_iter()], [0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 15, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29]) def test_fill_cache(self): results = self.msqs.all() self.assertEqual(len(results._result_cache), 0) results._fill_cache() self.assertEqual(len(results._result_cache), 10) results._fill_cache() self.assertEqual(len(results._result_cache), 20) # Test to ensure we properly fill the cache, even if we get fewer # results back (not in the SearchSite) than the hit count indicates. results = self.mmsqs.all() self.assertEqual(len(results._result_cache), 0) self.assertEqual([result.pk for result in results._result_cache], []) results._fill_cache() self.assertEqual(len(results._result_cache), 10) self.assertEqual([result.pk for result in results._result_cache], [0, 1, 2, 3, 4, 5, 6, 7, 8, 10]) results._fill_cache() self.assertEqual(len(results._result_cache), 20) self.assertEqual([result.pk for result in results._result_cache], [0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 15, 17, 18, 19, 20, 21, 22]) results._fill_cache() self.assertEqual(len(results._result_cache), 27) self.assertEqual([result.pk for result in results._result_cache], [0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 15, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29]) def test_cache_is_full(self): # Dummy always has a count of 0 and an empty _result_cache, hence True. self.assertEqual(self.bsqs._cache_is_full(), True) self.assertEqual(self.msqs._cache_is_full(), False) results = self.msqs.all() fire_the_iterator_and_fill_cache = [result for result in results] self.assertEqual(results._cache_is_full(), True) def test_all(self): sqs = self.bsqs.all() self.assert_(isinstance(sqs, SearchQuerySet)) def test_filter(self): sqs = self.bsqs.filter(content='foo') self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.query_filters), 1) def test_exclude(self): sqs = self.bsqs.exclude(content='foo') self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.query_filters), 1) def test_order_by(self): sqs = self.bsqs.order_by('foo') self.assert_(isinstance(sqs, SearchQuerySet)) self.assert_('foo' in sqs.query.order_by) def test_models(self): mock_index_site = SearchSite() mock_index_site.register(MockModel) mock_index_site.register(AnotherMockModel) bsqs = SearchQuerySet(site=mock_index_site) sqs = bsqs.all() self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.models), 0) sqs = bsqs.models(MockModel) self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.models), 1) sqs = bsqs.models(MockModel, AnotherMockModel) self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.models), 2) def test_boost(self): sqs = self.bsqs.boost('foo', 10) self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.boost.keys()), 1) def test_highlight(self): sqs = self.bsqs.highlight() self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual(sqs.query.highlight, True) def test_spelling(self): # Test the case where spelling support is disabled. sqs = self.bsqs.filter(content='Indx') self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual(sqs.spelling_suggestion(), None) def test_raw_search(self): self.assertEqual(len(self.bsqs.raw_search('foo')), 0) self.assertEqual(len(self.bsqs.raw_search('content__exact hello AND content__exact world')), 1) def test_load_all(self): # If nothing is registered, you get nothing. haystack.site.unregister(MockModel) sqs = self.msqs.load_all() self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs), 0) # For full tests, see the solr_backend. def test_load_all_queryset(self): sqs = self.msqs.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) # For full tests, see the solr_backend. def test_auto_query(self): sqs = self.bsqs.auto_query('test search -stuff') self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual([repr(the_filter) for the_filter in sqs.query.query_filters], ['<QueryFilter: AND content__exact=test>', '<QueryFilter: AND content__exact=search>', '<QueryFilter: NOT content__exact=stuff>']) sqs = self.bsqs.auto_query('test "my thing" search -stuff') self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual([repr(the_filter) for the_filter in sqs.query.query_filters], ['<QueryFilter: AND content__exact=my thing>', '<QueryFilter: AND content__exact=test>', '<QueryFilter: AND content__exact=search>', '<QueryFilter: NOT content__exact=stuff>']) sqs = self.bsqs.auto_query('test "my thing" search \'moar quotes\' -stuff') self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual([repr(the_filter) for the_filter in sqs.query.query_filters], ['<QueryFilter: AND content__exact=my thing>', '<QueryFilter: AND content__exact=moar quotes>', '<QueryFilter: AND content__exact=test>', '<QueryFilter: AND content__exact=search>', '<QueryFilter: NOT content__exact=stuff>']) sqs = self.bsqs.auto_query('test "my thing" search \'moar quotes\' "foo -stuff') self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual([repr(the_filter) for the_filter in sqs.query.query_filters], ['<QueryFilter: AND content__exact=my thing>', '<QueryFilter: AND content__exact=moar quotes>', '<QueryFilter: AND content__exact=test>', '<QueryFilter: AND content__exact=search>', '<QueryFilter: AND content__exact="foo>', '<QueryFilter: NOT content__exact=stuff>']) sqs = self.bsqs.auto_query('test - stuff') self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual([repr(the_filter) for the_filter in sqs.query.query_filters], ['<QueryFilter: AND content__exact=test>', '<QueryFilter: AND content__exact=->', '<QueryFilter: AND content__exact=stuff>']) def test_count(self): self.assertEqual(self.bsqs.count(), 0) def test_facet_counts(self): self.assertEqual(self.bsqs.facet_counts(), {}) def test_best_match(self): self.assert_(isinstance(self.msqs.best_match(), SearchResult)) def test_latest(self): self.assert_(isinstance(self.msqs.latest('pub_date'), SearchResult)) def test_more_like_this(self): mock = MockModel() mock.id = 1 self.assertEqual(len(self.msqs.more_like_this(mock)), 100) def test_facets(self): sqs = self.bsqs.facet('foo') self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.facets), 1) sqs2 = self.bsqs.facet('foo').facet('bar') self.assert_(isinstance(sqs2, SearchQuerySet)) self.assertEqual(len(sqs2.query.facets), 2) def test_date_facets(self): sqs = self.bsqs.date_facet('foo', start_date=datetime.date(2008, 2, 25), end_date=datetime.date(2009, 2, 25), gap_by='month') self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.date_facets), 1) sqs2 = self.bsqs.date_facet('foo', start_date=datetime.date(2008, 2, 25), end_date=datetime.date(2009, 2, 25), gap_by='month').date_facet('bar', start_date=datetime.date(2007, 2, 25), end_date=datetime.date(2009, 2, 25), gap_by='year') self.assert_(isinstance(sqs2, SearchQuerySet)) self.assertEqual(len(sqs2.query.date_facets), 2) def test_query_facets(self): sqs = self.bsqs.query_facet('foo', '[bar TO *]') self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.query_facets), 1) sqs2 = self.bsqs.query_facet('foo', '[bar TO *]').query_facet('bar', '[100 TO 499]') self.assert_(isinstance(sqs2, SearchQuerySet)) self.assertEqual(len(sqs2.query.query_facets), 2) def test_narrow(self): sqs = self.bsqs.narrow('foo:moof') self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.narrow_queries), 1) def test_clone(self): results = self.msqs.filter(foo='bar', foo__lt='10') clone = results._clone() self.assert_(isinstance(clone, SearchQuerySet)) self.assertEqual(clone.site, results.site) self.assertEqual(str(clone.query), str(results.query)) self.assertEqual(clone._result_cache, []) self.assertEqual(clone._result_count, None) self.assertEqual(clone._cache_full, False) def test_chaining(self): sqs = self.bsqs.filter(content='foo') self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.query_filters), 1) # A second instance should inherit none of the changes from above. sqs = self.bsqs.filter(content='bar') self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.query_filters), 1) def test_none(self): sqs = self.bsqs.none() self.assert_(isinstance(sqs, EmptySearchQuerySet)) self.assertEqual(len(sqs), 0)
class SearchQuerySetTestCase(TestCase): def setUp(self): super(SearchQuerySetTestCase, self).setUp() self.bsqs = SearchQuerySet(query=DummySearchQuery(backend=DummySearchBackend())) self.msqs = SearchQuerySet(query=MockSearchQuery(backend=MockSearchBackend())) def test_len(self): # Dummy always returns 0. self.assertEqual(len(self.bsqs), 0) self.assertEqual(len(self.msqs), 100) def test_iter(self): # Dummy always returns []. self.assertEqual([result for result in self.bsqs.all()], []) msqs = self.msqs.all() results = [result for result in msqs] self.assertEqual(results, MOCK_SEARCH_RESULTS) def test_slice(self): self.assertEqual(self.msqs.all()[1:11], MOCK_SEARCH_RESULTS[1:11]) self.assertEqual(self.msqs.all()[50], MOCK_SEARCH_RESULTS[50]) def test_manual_iter(self): results = self.msqs.all() for offset, result in enumerate(results._manual_iter()): self.assertEqual(result, MOCK_SEARCH_RESULTS[offset]) def test_fill_cache(self): results = self.msqs.all() self.assertEqual(len(results._result_cache), 0) results._fill_cache() self.assertEqual(len(results._result_cache), 20) def test_cache_is_full(self): # Dummy always has a count of 0 and an empty _result_cache, hence True. self.assertEqual(self.bsqs._cache_is_full(), True) self.assertEqual(self.msqs._cache_is_full(), False) results = self.msqs.all() fire_the_iterator_and_fill_cache = [result for result in results] self.assertEqual(results._cache_is_full(), True) def test_all(self): sqs = self.bsqs.all() self.assert_(isinstance(sqs, SearchQuerySet)) def test_filter(self): sqs = self.bsqs.filter(content='foo') self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.query_filters), 1) def test_exclude(self): sqs = self.bsqs.exclude(content='foo') self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.query_filters), 1) def test_order_by(self): sqs = self.bsqs.order_by('foo') self.assert_(isinstance(sqs, SearchQuerySet)) self.assert_('foo' in sqs.query.order_by) def test_models(self): mock_index_site = SearchSite() mock_index_site.register(MockModel) mock_index_site.register(AnotherMockModel) bsqs = SearchQuerySet(site=mock_index_site) sqs = bsqs.all() self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.models), 0) sqs = bsqs.models(MockModel) self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.models), 1) sqs = bsqs.models(MockModel, AnotherMockModel) self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.models), 2) def test_boost(self): sqs = self.bsqs.boost(foo=10) self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.boost.keys()), 1) def test_highlight(self): sqs = self.bsqs.highlight() self.assert_(isinstance(sqs, SearchQuerySet)) self.assert_(sqs.query.highlight, True) def test_raw_search(self): self.assertEqual(len(self.bsqs.raw_search('foo')), 0) self.assertEqual(len(self.bsqs.raw_search('content__exact hello AND content__exact world')), 1) def test_load_all(self): sqs = self.msqs.load_all() self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual(sqs[0].object.foo, 'bar') def test_auto_query(self): sqs = self.bsqs.auto_query('test search -stuff') self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual([repr(the_filter) for the_filter in sqs.query.query_filters], ['<QueryFilter: AND content__exact=test>', '<QueryFilter: AND content__exact=search>', '<QueryFilter: NOT content__exact=stuff>']) sqs = self.bsqs.auto_query('test "my thing" search -stuff') self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual([repr(the_filter) for the_filter in sqs.query.query_filters], ['<QueryFilter: AND content__exact=my thing>', '<QueryFilter: AND content__exact=test>', '<QueryFilter: AND content__exact=search>', '<QueryFilter: NOT content__exact=stuff>']) sqs = self.bsqs.auto_query('test "my thing" search \'moar quotes\' -stuff') self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual([repr(the_filter) for the_filter in sqs.query.query_filters], ['<QueryFilter: AND content__exact=my thing>', '<QueryFilter: AND content__exact=moar quotes>', '<QueryFilter: AND content__exact=test>', '<QueryFilter: AND content__exact=search>', '<QueryFilter: NOT content__exact=stuff>']) sqs = self.bsqs.auto_query('test "my thing" search \'moar quotes\' "foo -stuff') self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual([repr(the_filter) for the_filter in sqs.query.query_filters], ['<QueryFilter: AND content__exact=my thing>', '<QueryFilter: AND content__exact=moar quotes>', '<QueryFilter: AND content__exact=test>', '<QueryFilter: AND content__exact=search>', '<QueryFilter: AND content__exact="foo>', '<QueryFilter: NOT content__exact=stuff>']) def test_count(self): self.assertEqual(self.bsqs.count(), 0) def test_facet_counts(self): self.assertEqual(self.bsqs.facet_counts(), {}) def test_best_match(self): self.assert_(isinstance(self.msqs.best_match(), SearchResult)) def test_latest(self): self.assert_(isinstance(self.msqs.latest('pub_date'), SearchResult)) def test_more_like_this(self): mock = MockModel() mock.id = 1 self.assertEqual(len(self.msqs.more_like_this(mock)), 100) def test_facets(self): sqs = self.bsqs.facet('foo') self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.facets), 1) sqs2 = self.bsqs.facet('foo').facet('bar') self.assert_(isinstance(sqs2, SearchQuerySet)) self.assertEqual(len(sqs2.query.facets), 2) def test_date_facets(self): sqs = self.bsqs.date_facet('foo', start_date=datetime.date(2008, 2, 25), end_date=datetime.date(2009, 2, 25), gap='/MONTH') self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.date_facets), 1) sqs2 = self.bsqs.date_facet('foo', start_date=datetime.date(2008, 2, 25), end_date=datetime.date(2009, 2, 25), gap='/MONTH').date_facet('bar', start_date=datetime.date(2007, 2, 25), end_date=datetime.date(2009, 2, 25), gap='/YEAR') self.assert_(isinstance(sqs2, SearchQuerySet)) self.assertEqual(len(sqs2.query.date_facets), 2) def test_query_facets(self): sqs = self.bsqs.query_facet('foo', '[bar TO *]') self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.query_facets), 1) sqs2 = self.bsqs.query_facet('foo', '[bar TO *]').query_facet('bar', '[100 TO 499]') self.assert_(isinstance(sqs2, SearchQuerySet)) self.assertEqual(len(sqs2.query.query_facets), 2) def test_narrow(self): sqs = self.bsqs.narrow('foo:moof') self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.narrow_queries), 1) def test_clone(self): results = self.msqs.filter(foo='bar', foo__lt='10') clone = results._clone() self.assert_(isinstance(clone, SearchQuerySet)) self.assertEqual(clone.site, results.site) self.assertEqual(str(clone.query), str(results.query)) self.assertEqual(clone._result_cache, []) self.assertEqual(clone._result_count, None) self.assertEqual(clone._cache_full, False) def test_chaining(self): sqs = self.bsqs.filter(content='foo') self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.query_filters), 1) # A second instance should inherit none of the changes from above. sqs = self.bsqs.filter(content='bar') self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.query_filters), 1)
class SearchQuerySetTestCase(TestCase): fixtures = ['bulk_data.json'] def setUp(self): super(SearchQuerySetTestCase, self).setUp() # Stow. self.old_unified_index = connections['default']._index self.ui = UnifiedIndex() self.bmmsi = BasicMockModelSearchIndex() self.cpkmmsi = CharPKMockModelSearchIndex() self.ui.build(indexes=[self.bmmsi, self.cpkmmsi]) connections['default']._index = self.ui # Update the "index". backend = connections['default'].get_backend() backend.clear() backend.update(self.bmmsi, MockModel.objects.all()) self.msqs = SearchQuerySet() # Stow. self.old_debug = settings.DEBUG settings.DEBUG = True reset_search_queries() def tearDown(self): # Restore. connections['default']._index = self.old_unified_index settings.DEBUG = self.old_debug super(SearchQuerySetTestCase, self).tearDown() def test_len(self): self.assertEqual(len(self.msqs), 23) def test_repr(self): reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) self.assertEqual( repr(self.msqs), "[<SearchResult: core.mockmodel (pk=u'1')>, <SearchResult: core.mockmodel (pk=u'2')>, <SearchResult: core.mockmodel (pk=u'3')>, <SearchResult: core.mockmodel (pk=u'4')>, <SearchResult: core.mockmodel (pk=u'5')>, <SearchResult: core.mockmodel (pk=u'6')>, <SearchResult: core.mockmodel (pk=u'7')>, <SearchResult: core.mockmodel (pk=u'8')>, <SearchResult: core.mockmodel (pk=u'9')>, <SearchResult: core.mockmodel (pk=u'10')>, <SearchResult: core.mockmodel (pk=u'11')>, <SearchResult: core.mockmodel (pk=u'12')>, <SearchResult: core.mockmodel (pk=u'13')>, <SearchResult: core.mockmodel (pk=u'14')>, <SearchResult: core.mockmodel (pk=u'15')>, <SearchResult: core.mockmodel (pk=u'16')>, <SearchResult: core.mockmodel (pk=u'17')>, <SearchResult: core.mockmodel (pk=u'18')>, <SearchResult: core.mockmodel (pk=u'19')>, '...(remaining elements truncated)...']" ) self.assertEqual(len(connections['default'].queries), 1) def test_iter(self): reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) msqs = self.msqs.all() results = [int(res.pk) for res in msqs] self.assertEqual(results, [res.pk for res in MOCK_SEARCH_RESULTS[:23]]) self.assertEqual(len(connections['default'].queries), 3) def test_slice(self): reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) results = self.msqs.all() self.assertEqual([int(res.pk) for res in results[1:11]], [res.pk for res in MOCK_SEARCH_RESULTS[1:11]]) self.assertEqual(len(connections['default'].queries), 1) reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) results = self.msqs.all() self.assertEqual(int(results[22].pk), MOCK_SEARCH_RESULTS[22].pk) self.assertEqual(len(connections['default'].queries), 1) def test_manual_iter(self): results = self.msqs.all() reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) check = [result.pk for result in results._manual_iter()] self.assertEqual(check, [ u'1', u'2', u'3', u'4', u'5', u'6', u'7', u'8', u'9', u'10', u'11', u'12', u'13', u'14', u'15', u'16', u'17', u'18', u'19', u'20', u'21', u'22', u'23' ]) self.assertEqual(len(connections['default'].queries), 3) reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) # Test to ensure we properly fill the cache, even if we get fewer # results back (not a handled model) than the hit count indicates. # This will hang indefinitely if broken. old_ui = self.ui self.ui.build(indexes=[self.cpkmmsi]) connections['default']._index = self.ui self.cpkmmsi.update() results = self.msqs.all() loaded = [result.pk for result in results._manual_iter()] self.assertEqual(loaded, [u'sometext', u'1234']) self.assertEqual(len(connections['default'].queries), 1) connections['default']._index = old_ui def test_fill_cache(self): reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) results = self.msqs.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) reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) # Test to ensure we properly fill the cache, even if we get fewer # results back (not a handled model) than the hit count indicates. sqs = SearchQuerySet().all() sqs.query.backend = MixedMockSearchBackend('default') results = sqs self.assertEqual( len([ result for result in results._result_cache if result is not None ]), 0) self.assertEqual([ int(result.pk) for result in results._result_cache if result is not None ], []) 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 ]), 9) self.assertEqual([ int(result.pk) for result in results._result_cache if result is not None ], [1, 2, 3, 4, 5, 6, 7, 8, 10]) self.assertEqual(len(connections['default'].queries), 2) results._fill_cache(10, 20) self.assertEqual( len([ result for result in results._result_cache if result is not None ]), 17) self.assertEqual([ int(result.pk) for result in results._result_cache if result is not None ], [1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 15, 16, 17, 18, 19, 20]) self.assertEqual(len(connections['default'].queries), 4) results._fill_cache(20, 30) self.assertEqual( len([ result for result in results._result_cache if result is not None ]), 20) self.assertEqual([ int(result.pk) for result in results._result_cache if result is not None ], [ 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 15, 16, 17, 18, 19, 20, 21, 22, 23 ]) self.assertEqual(len(connections['default'].queries), 6) def test_cache_is_full(self): reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) self.assertEqual(self.msqs._cache_is_full(), False) results = self.msqs.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_all(self): sqs = self.msqs.all() self.assertTrue(isinstance(sqs, SearchQuerySet)) def test_filter(self): sqs = self.msqs.filter(content='foo') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.query_filter), 1) def test_exclude(self): sqs = self.msqs.exclude(content='foo') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.query_filter), 1) def test_order_by(self): sqs = self.msqs.order_by('foo') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertTrue('foo' in sqs.query.order_by) def test_models(self): # Stow. old_unified_index = connections['default']._index ui = UnifiedIndex() bmmsi = BasicMockModelSearchIndex() bammsi = BasicAnotherMockModelSearchIndex() ui.build(indexes=[bmmsi, bammsi]) connections['default']._index = ui msqs = SearchQuerySet() sqs = msqs.all() self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.models), 0) sqs = msqs.models(MockModel) self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.models), 1) sqs = msqs.models(MockModel, AnotherMockModel) self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.models), 2) # This will produce a warning. ui.build(indexes=[bmmsi]) sqs = msqs.models(AnotherMockModel) self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.models), 1) def test_result_class(self): sqs = self.msqs.all() self.assertTrue(issubclass(sqs.query.result_class, SearchResult)) # Custom class. class IttyBittyResult(object): pass sqs = self.msqs.result_class(IttyBittyResult) self.assertTrue(issubclass(sqs.query.result_class, IttyBittyResult)) # Reset to default. sqs = self.msqs.result_class(None) self.assertTrue(issubclass(sqs.query.result_class, SearchResult)) def test_boost(self): sqs = self.msqs.boost('foo', 10) self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.boost.keys()), 1) def test_highlight(self): sqs = self.msqs.highlight() self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(sqs.query.highlight, True) def test_spelling(self): # Test the case where spelling support is disabled. sqs = self.msqs.filter(content='Indx') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(sqs.spelling_suggestion(), None) self.assertEqual(sqs.spelling_suggestion('indexy'), None) def test_raw_search(self): self.assertEqual(len(self.msqs.raw_search('foo')), 23) self.assertEqual( len( self.msqs.raw_search( '(content__exact:hello AND content__exact:world)')), 23) def test_load_all(self): # Models with character primary keys. sqs = SearchQuerySet() sqs.query.backend = CharPKMockSearchBackend('charpk') results = sqs.load_all().all() self.assertEqual(len(results._result_cache), 0) results._fill_cache(0, 2) self.assertEqual( len([ result for result in results._result_cache if result is not None ]), 2) # If nothing is handled, you get nothing. old_ui = connections['default']._index ui = UnifiedIndex() ui.build(indexes=[]) connections['default']._index = ui sqs = self.msqs.load_all() self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs), 0) connections['default']._index = old_ui # For full tests, see the solr_backend. def test_load_all_read_queryset(self): # Stow. old_ui = connections['default']._index ui = UnifiedIndex() gafmmsi = GhettoAFifthMockModelSearchIndex() ui.build(indexes=[gafmmsi]) connections['default']._index = ui gafmmsi.update() sqs = SearchQuerySet() results = sqs.load_all().all() results.query.backend = ReadQuerySetMockSearchBackend('default') results._fill_cache(0, 2) # The deleted result isn't returned self.assertEqual( len([ result for result in results._result_cache if result is not None ]), 1) # Register a SearchIndex with a read_queryset that returns deleted items rqstsi = TextReadQuerySetTestSearchIndex() ui.build(indexes=[rqstsi]) rqstsi.update() sqs = SearchQuerySet() results = sqs.load_all().all() results.query.backend = ReadQuerySetMockSearchBackend('default') results._fill_cache(0, 2) # Both the deleted and not deleted items are returned self.assertEqual( len([ result for result in results._result_cache if result is not None ]), 2) # Restore. connections['default']._index = old_ui def test_auto_query(self): sqs = self.msqs.auto_query('test search -stuff') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual( repr(sqs.query.query_filter), '<SQ: AND (content__contains=test AND content__contains=search AND NOT (content__contains=stuff))>' ) sqs = self.msqs.auto_query('test "my thing" search -stuff') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual( repr(sqs.query.query_filter), '<SQ: AND (content__exact=my thing AND content__contains=test AND content__contains=search AND NOT (content__contains=stuff))>' ) sqs = self.msqs.auto_query( 'test "my thing" search \'moar quotes\' -stuff') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual( repr(sqs.query.query_filter), "<SQ: AND (content__exact=my thing AND content__contains=test AND content__contains=search AND content__contains='moar AND content__contains=quotes' AND NOT (content__contains=stuff))>" ) sqs = self.msqs.auto_query( 'test "my thing" search \'moar quotes\' "foo -stuff') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual( repr(sqs.query.query_filter), '<SQ: AND (content__exact=my thing AND content__contains=test AND content__contains=search AND content__contains=\'moar AND content__contains=quotes\' AND content__contains="foo AND NOT (content__contains=stuff))>' ) sqs = self.msqs.auto_query('test - stuff') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual( repr(sqs.query.query_filter), '<SQ: AND (content__contains=test AND content__contains=- AND content__contains=stuff)>' ) # Ensure bits in exact matches get escaped properly as well. sqs = self.msqs.auto_query('"pants:rule"') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(repr(sqs.query.query_filter), '<SQ: AND content__exact=pants:rule>') # Now with a different fieldname sqs = self.msqs.auto_query('test search -stuff', fieldname='title') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual( repr(sqs.query.query_filter), '<SQ: AND (title__contains=test AND title__contains=search AND NOT (title__contains=stuff))>' ) sqs = self.msqs.auto_query('test "my thing" search -stuff', fieldname='title') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual( repr(sqs.query.query_filter), '<SQ: AND (title__exact=my thing AND title__contains=test AND title__contains=search AND NOT (title__contains=stuff))>' ) def test_count(self): self.assertEqual(self.msqs.count(), 23) def test_facet_counts(self): self.assertEqual(self.msqs.facet_counts(), {}) def test_best_match(self): self.assertTrue(isinstance(self.msqs.best_match(), SearchResult)) def test_latest(self): self.assertTrue(isinstance(self.msqs.latest('pub_date'), SearchResult)) def test_more_like_this(self): mock = MockModel() mock.id = 1 self.assertEqual(len(self.msqs.more_like_this(mock)), 23) def test_facets(self): sqs = self.msqs.facet('foo') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.facets), 1) sqs2 = self.msqs.facet('foo').facet('bar') self.assertTrue(isinstance(sqs2, SearchQuerySet)) self.assertEqual(len(sqs2.query.facets), 2) def test_date_facets(self): try: sqs = self.msqs.date_facet('foo', start_date=datetime.date(2008, 2, 25), end_date=datetime.date(2009, 2, 25), gap_by='smarblaph') self.fail() except FacetingError, e: self.assertEqual( str(e), "The gap_by ('smarblaph') must be one of the following: year, month, day, hour, minute, second." ) sqs = self.msqs.date_facet('foo', start_date=datetime.date(2008, 2, 25), end_date=datetime.date(2009, 2, 25), gap_by='month') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.date_facets), 1) sqs2 = self.msqs.date_facet('foo', start_date=datetime.date(2008, 2, 25), end_date=datetime.date(2009, 2, 25), gap_by='month').date_facet( 'bar', start_date=datetime.date(2007, 2, 25), end_date=datetime.date(2009, 2, 25), gap_by='year') self.assertTrue(isinstance(sqs2, SearchQuerySet)) self.assertEqual(len(sqs2.query.date_facets), 2)
class SearchQuerySetTestCase(TestCase): fixtures = ["base_data.json", "bulk_data.json"] def setUp(self): super(SearchQuerySetTestCase, self).setUp() # Stow. self.old_unified_index = connections["default"]._index self.ui = UnifiedIndex() self.bmmsi = BasicMockModelSearchIndex() self.cpkmmsi = CharPKMockModelSearchIndex() self.uuidmmsi = SimpleMockUUIDModelIndex() self.ui.build(indexes=[self.bmmsi, self.cpkmmsi, self.uuidmmsi]) connections["default"]._index = self.ui # Update the "index". backend = connections["default"].get_backend() backend.clear() backend.update(self.bmmsi, MockModel.objects.all()) self.msqs = SearchQuerySet() # Stow. reset_search_queries() def tearDown(self): # Restore. connections["default"]._index = self.old_unified_index super(SearchQuerySetTestCase, self).tearDown() def test_len(self): self.assertEqual(len(self.msqs), 23) def test_repr(self): reset_search_queries() self.assertEqual(len(connections["default"].queries), 0) self.assertRegexpMatches( repr(self.msqs), r"^<SearchQuerySet: query=<test_haystack.mocks.MockSearchQuery object" r" at 0x[0-9A-Fa-f]+>, using=None>$", ) def test_iter(self): reset_search_queries() self.assertEqual(len(connections["default"].queries), 0) msqs = self.msqs.all() results = [int(res.pk) for res in iter(msqs)] self.assertEqual(results, [res.pk for res in MOCK_SEARCH_RESULTS[:23]]) self.assertEqual(len(connections["default"].queries), 3) def test_slice(self): reset_search_queries() self.assertEqual(len(connections["default"].queries), 0) results = self.msqs.all() self.assertEqual( [int(res.pk) for res in results[1:11]], [res.pk for res in MOCK_SEARCH_RESULTS[1:11]], ) self.assertEqual(len(connections["default"].queries), 1) reset_search_queries() self.assertEqual(len(connections["default"].queries), 0) results = self.msqs.all() self.assertEqual(int(results[22].pk), MOCK_SEARCH_RESULTS[22].pk) self.assertEqual(len(connections["default"].queries), 1) def test_manual_iter(self): results = self.msqs.all() reset_search_queries() self.assertEqual(len(connections["default"].queries), 0) check = [result.pk for result in results._manual_iter()] self.assertEqual( check, [ "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", ], ) self.assertEqual(len(connections["default"].queries), 3) reset_search_queries() self.assertEqual(len(connections["default"].queries), 0) # Test to ensure we properly fill the cache, even if we get fewer # results back (not a handled model) than the hit count indicates. # This will hang indefinitely if broken. # CharPK testing old_ui = self.ui self.ui.build(indexes=[self.cpkmmsi]) connections["default"]._index = self.ui self.cpkmmsi.update() results = self.msqs.all() loaded = [result.pk for result in results._manual_iter()] self.assertEqual(loaded, ["sometext", "1234"]) self.assertEqual(len(connections["default"].queries), 1) # UUID testing self.ui.build(indexes=[self.uuidmmsi]) connections["default"]._index = self.ui self.uuidmmsi.update() results = self.msqs.all() loaded = [result.pk for result in results._manual_iter()] self.assertEqual( loaded, [ "53554c58-7051-4350-bcc9-dad75eb248a9", "77554c58-7051-4350-bcc9-dad75eb24888", ], ) connections["default"]._index = old_ui def test_cache_is_full(self): reset_search_queries() self.assertEqual(len(connections["default"].queries), 0) self.assertEqual(self.msqs._cache_is_full(), False) results = self.msqs.all() fire_the_iterator_and_fill_cache = list(results) self.assertEqual(23, len(fire_the_iterator_and_fill_cache)) self.assertEqual(results._cache_is_full(), True) self.assertEqual(len(connections["default"].queries), 4) def test_all(self): sqs = self.msqs.all() self.assertTrue(isinstance(sqs, SearchQuerySet)) def test_filter(self): sqs = self.msqs.filter(content="foo") self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.query_filter), 1) def test_exclude(self): sqs = self.msqs.exclude(content="foo") self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.query_filter), 1) def test_order_by(self): sqs = self.msqs.order_by("foo") self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertTrue("foo" in sqs.query.order_by) def test_models(self): # Stow. old_unified_index = connections["default"]._index ui = UnifiedIndex() bmmsi = BasicMockModelSearchIndex() bammsi = BasicAnotherMockModelSearchIndex() ui.build(indexes=[bmmsi, bammsi]) connections["default"]._index = ui msqs = SearchQuerySet() sqs = msqs.all() self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.models), 0) sqs = msqs.models(MockModel) self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.models), 1) sqs = msqs.models(MockModel, AnotherMockModel) self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.models), 2) # This will produce a warning. ui.build(indexes=[bmmsi]) sqs = msqs.models(AnotherMockModel) self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.models), 1) def test_result_class(self): sqs = self.msqs.all() self.assertTrue(issubclass(sqs.query.result_class, SearchResult)) # Custom class. class IttyBittyResult(object): pass sqs = self.msqs.result_class(IttyBittyResult) self.assertTrue(issubclass(sqs.query.result_class, IttyBittyResult)) # Reset to default. sqs = self.msqs.result_class(None) self.assertTrue(issubclass(sqs.query.result_class, SearchResult)) def test_boost(self): sqs = self.msqs.boost("foo", 10) self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.boost.keys()), 1) def test_highlight(self): sqs = self.msqs.highlight() self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(sqs.query.highlight, True) def test_spelling_override(self): sqs = self.msqs.filter(content="not the spellchecking query") self.assertEqual(sqs.query.spelling_query, None) sqs = self.msqs.set_spelling_query("override") self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(sqs.query.spelling_query, "override") def test_spelling_suggestions(self): # Test the case where spelling support is disabled. sqs = self.msqs.filter(content="Indx") self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(sqs.spelling_suggestion(), None) self.assertEqual(sqs.spelling_suggestion("indexy"), None) def test_raw_search(self): self.assertEqual(len(self.msqs.raw_search("foo")), 23) self.assertEqual( len( self.msqs.raw_search("(content__exact:hello AND content__exact:world)") ), 23, ) def test_load_all(self): # Models with character primary keys. sqs = SearchQuerySet() sqs.query.backend = CharPKMockSearchBackend("charpk") results = sqs.load_all().all() self.assertEqual(len(results._result_cache), 0) results._fill_cache(0, 2) self.assertEqual( len([result for result in results._result_cache if result is not None]), 2 ) # Models with uuid primary keys. sqs = SearchQuerySet() sqs.query.backend = UUIDMockSearchBackend("uuid") results = sqs.load_all().all() self.assertEqual(len(results._result_cache), 0) results._fill_cache(0, 2) self.assertEqual( len([result for result in results._result_cache if result is not None]), 2 ) # If nothing is handled, you get nothing. old_ui = connections["default"]._index ui = UnifiedIndex() ui.build(indexes=[]) connections["default"]._index = ui sqs = self.msqs.load_all() self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs), 0) connections["default"]._index = old_ui # For full tests, see the solr_backend. def test_load_all_read_queryset(self): # Stow. old_ui = connections["default"]._index ui = UnifiedIndex() gafmmsi = GhettoAFifthMockModelSearchIndex() ui.build(indexes=[gafmmsi]) connections["default"]._index = ui gafmmsi.update() sqs = SearchQuerySet() results = sqs.load_all().all() results.query.backend = ReadQuerySetMockSearchBackend("default") results._fill_cache(0, 2) # The deleted result isn't returned self.assertEqual( len([result for result in results._result_cache if result is not None]), 1 ) # Register a SearchIndex with a read_queryset that returns deleted items rqstsi = TextReadQuerySetTestSearchIndex() ui.build(indexes=[rqstsi]) rqstsi.update() sqs = SearchQuerySet() results = sqs.load_all().all() results.query.backend = ReadQuerySetMockSearchBackend("default") results._fill_cache(0, 2) # Both the deleted and not deleted items are returned self.assertEqual( len([result for result in results._result_cache if result is not None]), 2 ) # Restore. connections["default"]._index = old_ui def test_auto_query(self): sqs = self.msqs.auto_query("test search -stuff") self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual( repr(sqs.query.query_filter), "<SQ: AND content__content=test search -stuff>", ) sqs = self.msqs.auto_query('test "my thing" search -stuff') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual( repr(sqs.query.query_filter), '<SQ: AND content__content=test "my thing" search -stuff>', ) sqs = self.msqs.auto_query("test \"my thing\" search 'moar quotes' -stuff") self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual( repr(sqs.query.query_filter), "<SQ: AND content__content=test \"my thing\" search 'moar quotes' -stuff>", ) sqs = self.msqs.auto_query('test "my thing" search \'moar quotes\' "foo -stuff') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual( repr(sqs.query.query_filter), '<SQ: AND content__content=test "my thing" search \'moar quotes\' "foo -stuff>', ) sqs = self.msqs.auto_query("test - stuff") self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual( repr(sqs.query.query_filter), "<SQ: AND content__content=test - stuff>" ) # Ensure bits in exact matches get escaped properly as well. sqs = self.msqs.auto_query('"pants:rule"') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual( repr(sqs.query.query_filter), '<SQ: AND content__content="pants:rule">' ) # Now with a different fieldname sqs = self.msqs.auto_query("test search -stuff", fieldname="title") self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual( repr(sqs.query.query_filter), "<SQ: AND title__content=test search -stuff>" ) sqs = self.msqs.auto_query('test "my thing" search -stuff', fieldname="title") self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual( repr(sqs.query.query_filter), '<SQ: AND title__content=test "my thing" search -stuff>', ) def test_count(self): self.assertEqual(self.msqs.count(), 23) def test_facet_counts(self): self.assertEqual(self.msqs.facet_counts(), {}) def test_best_match(self): self.assertTrue(isinstance(self.msqs.best_match(), SearchResult)) def test_latest(self): self.assertTrue(isinstance(self.msqs.latest("pub_date"), SearchResult)) def test_more_like_this(self): mock = MockModel() mock.id = 1 self.assertEqual(len(self.msqs.more_like_this(mock)), 23) def test_facets(self): sqs = self.msqs.facet("foo") self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.facets), 1) sqs2 = self.msqs.facet("foo").facet("bar") self.assertTrue(isinstance(sqs2, SearchQuerySet)) self.assertEqual(len(sqs2.query.facets), 2) def test_date_facets(self): try: sqs = self.msqs.date_facet( "foo", start_date=datetime.date(2008, 2, 25), end_date=datetime.date(2009, 2, 25), gap_by="smarblaph", ) self.fail() except FacetingError as e: self.assertEqual( str(e), "The gap_by ('smarblaph') must be one of the following: year, month, day, hour, minute, second.", ) sqs = self.msqs.date_facet( "foo", start_date=datetime.date(2008, 2, 25), end_date=datetime.date(2009, 2, 25), gap_by="month", ) self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.date_facets), 1) sqs2 = self.msqs.date_facet( "foo", start_date=datetime.date(2008, 2, 25), end_date=datetime.date(2009, 2, 25), gap_by="month", ).date_facet( "bar", start_date=datetime.date(2007, 2, 25), end_date=datetime.date(2009, 2, 25), gap_by="year", ) self.assertTrue(isinstance(sqs2, SearchQuerySet)) self.assertEqual(len(sqs2.query.date_facets), 2) def test_query_facets(self): sqs = self.msqs.query_facet("foo", "[bar TO *]") self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.query_facets), 1) sqs2 = self.msqs.query_facet("foo", "[bar TO *]").query_facet( "bar", "[100 TO 499]" ) self.assertTrue(isinstance(sqs2, SearchQuerySet)) self.assertEqual(len(sqs2.query.query_facets), 2) # Test multiple query facets on a single field sqs3 = ( self.msqs.query_facet("foo", "[bar TO *]") .query_facet("bar", "[100 TO 499]") .query_facet("foo", "[1000 TO 1499]") ) self.assertTrue(isinstance(sqs3, SearchQuerySet)) self.assertEqual(len(sqs3.query.query_facets), 3) def test_stats(self): sqs = self.msqs.stats_facet("foo", "bar") self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.stats), 1) sqs2 = self.msqs.stats_facet("foo", "bar").stats_facet("foo", "baz") self.assertTrue(isinstance(sqs2, SearchQuerySet)) self.assertEqual(len(sqs2.query.stats), 1) sqs3 = self.msqs.stats_facet("foo", "bar").stats_facet("moof", "baz") self.assertTrue(isinstance(sqs3, SearchQuerySet)) self.assertEqual(len(sqs3.query.stats), 2) def test_narrow(self): sqs = self.msqs.narrow("foo:moof") self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.narrow_queries), 1) def test_clone(self): results = self.msqs.filter(foo="bar", foo__lt="10") clone = results._clone() self.assertTrue(isinstance(clone, SearchQuerySet)) self.assertEqual(str(clone.query), str(results.query)) self.assertEqual(clone._result_cache, []) self.assertEqual(clone._result_count, None) self.assertEqual(clone._cache_full, False) self.assertEqual(clone._using, results._using) def test_using(self): sqs = SearchQuerySet(using="default") self.assertNotEqual(sqs.query, None) self.assertEqual(sqs.query._using, "default") def test_chaining(self): sqs = self.msqs.filter(content="foo") self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.query_filter), 1) # A second instance should inherit none of the changes from above. sqs = self.msqs.filter(content="bar") self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.query_filter), 1) def test_none(self): sqs = self.msqs.none() self.assertTrue(isinstance(sqs, EmptySearchQuerySet)) self.assertEqual(len(sqs), 0) def test___and__(self): sqs1 = self.msqs.filter(content="foo") sqs2 = self.msqs.filter(content="bar") sqs = sqs1 & sqs2 self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.query_filter), 2) def test___or__(self): sqs1 = self.msqs.filter(content="foo") sqs2 = self.msqs.filter(content="bar") sqs = sqs1 | sqs2 self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.query_filter), 2) def test_and_or(self): """ Combining AND queries with OR should give AND(OR(a, b), OR(c, d)) """ sqs1 = self.msqs.filter(content="foo").filter(content="oof") sqs2 = self.msqs.filter(content="bar").filter(content="rab") sqs = sqs1 | sqs2 self.assertEqual(sqs.query.query_filter.connector, "OR") self.assertEqual( repr(sqs.query.query_filter.children[0]), repr(sqs1.query.query_filter) ) self.assertEqual( repr(sqs.query.query_filter.children[1]), repr(sqs2.query.query_filter) ) def test_or_and(self): """ Combining OR queries with AND should give OR(AND(a, b), AND(c, d)) """ sqs1 = self.msqs.filter(content="foo").filter_or(content="oof") sqs2 = self.msqs.filter(content="bar").filter_or(content="rab") sqs = sqs1 & sqs2 self.assertEqual(sqs.query.query_filter.connector, "AND") self.assertEqual( repr(sqs.query.query_filter.children[0]), repr(sqs1.query.query_filter) ) self.assertEqual( repr(sqs.query.query_filter.children[1]), repr(sqs2.query.query_filter) )
class LiveSolrMoreLikeThisTestCase(TestCase): fixtures = ['bulk_data.json'] def setUp(self): super(LiveSolrMoreLikeThisTestCase, self).setUp() # Wipe it clean. clear_solr_index() self.old_ui = connections['default'].get_unified_index() self.ui = UnifiedIndex() self.smmi = SolrMockModelSearchIndex() self.sammi = SolrAnotherMockModelSearchIndex() self.ui.build(indexes=[self.smmi, self.sammi]) connections['default']._index = self.ui self.sqs = SearchQuerySet() self.smmi.update() self.sammi.update() def tearDown(self): # Restore. connections['default']._index = self.old_ui super(LiveSolrMoreLikeThisTestCase, self).tearDown() def test_more_like_this(self): all_mlt = self.sqs.more_like_this(MockModel.objects.get(pk=1)) self.assertEqual( all_mlt.count(), len([result.pk for result in all_mlt]), msg= "mlt SearchQuerySet .count() didn't match retrieved result length") # Rather than hard-code assumptions about Solr's return order, we have a few very similar # items which we'll confirm are included in the first 5 results. This is still ugly as we're # hard-coding primary keys but it's better than breaking any time a Solr update or data # change causes a score to shift slightly top_results = [int(result.pk) for result in all_mlt[:5]] for i in (14, 6, 4, 22, 10): self.assertIn(i, top_results) filtered_mlt = self.sqs.filter(name='daniel3').more_like_this( MockModel.objects.get(pk=3)) self.assertLess(filtered_mlt.count(), all_mlt.count()) top_filtered_results = [int(result.pk) for result in filtered_mlt[:5]] for i in (23, 13, 17, 16, 19): self.assertIn(i, top_filtered_results) filtered_mlt_with_models = self.sqs.models(MockModel).more_like_this( MockModel.objects.get(pk=1)) self.assertLessEqual(filtered_mlt_with_models.count(), all_mlt.count()) top_filtered_with_models = [ int(result.pk) for result in filtered_mlt_with_models[:5] ] for i in (14, 6, 4, 22, 10): self.assertIn(i, top_filtered_with_models) def test_more_like_this_defer(self): mi = MockModel.objects.defer('foo').get(pk=1) # FIXME: this currently is known to fail because haystack.utils.loading doesn't see the # MockModel_Deferred_foo class as registered: deferred = self.sqs.models(MockModel).more_like_this(mi) self.assertEqual(deferred.count(), 0) self.assertEqual([result.pk for result in deferred], []) self.assertEqual(len([result.pk for result in deferred]), 0) def test_more_like_this_custom_result_class(self): """Ensure that swapping the ``result_class`` works""" first_result = self.sqs.result_class(MockSearchResult).more_like_this( MockModel.objects.get(pk=1))[0] self.assertIsInstance(first_result, MockSearchResult)
class SearchQuerySetTestCase(TestCase): fixtures = ['bulk_data.json'] def setUp(self): super(SearchQuerySetTestCase, self).setUp() # Stow. self.old_unified_index = connections['default']._index self.ui = UnifiedIndex() self.bmmsi = BasicMockModelSearchIndex() self.cpkmmsi = CharPKMockModelSearchIndex() self.ui.build(indexes=[self.bmmsi, self.cpkmmsi]) connections['default']._index = self.ui # Update the "index". backend = connections['default'].get_backend() backend.clear() backend.update(self.bmmsi, MockModel.objects.all()) self.msqs = SearchQuerySet() # Stow. self.old_debug = settings.DEBUG settings.DEBUG = True reset_search_queries() def tearDown(self): # Restore. connections['default']._index = self.old_unified_index settings.DEBUG = self.old_debug super(SearchQuerySetTestCase, self).tearDown() def test_len(self): self.assertEqual(len(self.msqs), 23) def test_repr(self): reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) self.assertEqual(repr(self.msqs), "[<SearchResult: core.mockmodel (pk=u'1')>, <SearchResult: core.mockmodel (pk=u'2')>, <SearchResult: core.mockmodel (pk=u'3')>, <SearchResult: core.mockmodel (pk=u'4')>, <SearchResult: core.mockmodel (pk=u'5')>, <SearchResult: core.mockmodel (pk=u'6')>, <SearchResult: core.mockmodel (pk=u'7')>, <SearchResult: core.mockmodel (pk=u'8')>, <SearchResult: core.mockmodel (pk=u'9')>, <SearchResult: core.mockmodel (pk=u'10')>, <SearchResult: core.mockmodel (pk=u'11')>, <SearchResult: core.mockmodel (pk=u'12')>, <SearchResult: core.mockmodel (pk=u'13')>, <SearchResult: core.mockmodel (pk=u'14')>, <SearchResult: core.mockmodel (pk=u'15')>, <SearchResult: core.mockmodel (pk=u'16')>, <SearchResult: core.mockmodel (pk=u'17')>, <SearchResult: core.mockmodel (pk=u'18')>, <SearchResult: core.mockmodel (pk=u'19')>, '...(remaining elements truncated)...']") self.assertEqual(len(connections['default'].queries), 1) def test_iter(self): reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) msqs = self.msqs.all() results = [int(res.pk) for res in msqs] self.assertEqual(results, [res.pk for res in MOCK_SEARCH_RESULTS[:23]]) self.assertEqual(len(connections['default'].queries), 3) def test_slice(self): reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) results = self.msqs.all() self.assertEqual([int(res.pk) for res in results[1:11]], [res.pk for res in MOCK_SEARCH_RESULTS[1:11]]) self.assertEqual(len(connections['default'].queries), 1) reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) results = self.msqs.all() self.assertEqual(int(results[22].pk), MOCK_SEARCH_RESULTS[22].pk) self.assertEqual(len(connections['default'].queries), 1) def test_manual_iter(self): results = self.msqs.all() reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) check = [result.pk for result in results._manual_iter()] self.assertEqual(check, [u'1', u'2', u'3', u'4', u'5', u'6', u'7', u'8', u'9', u'10', u'11', u'12', u'13', u'14', u'15', u'16', u'17', u'18', u'19', u'20', u'21', u'22', u'23']) self.assertEqual(len(connections['default'].queries), 3) reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) # Test to ensure we properly fill the cache, even if we get fewer # results back (not a handled model) than the hit count indicates. # This will hang indefinitely if broken. old_ui = self.ui self.ui.build(indexes=[self.cpkmmsi]) connections['default']._index = self.ui self.cpkmmsi.update() results = self.msqs.all() loaded = [result.pk for result in results._manual_iter()] self.assertEqual(loaded, [u'sometext', u'1234']) self.assertEqual(len(connections['default'].queries), 1) connections['default']._index = old_ui def test_fill_cache(self): reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) results = self.msqs.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) reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) # Test to ensure we properly fill the cache, even if we get fewer # results back (not a handled model) than the hit count indicates. sqs = SearchQuerySet().all() sqs.query.backend = MixedMockSearchBackend('default') results = sqs self.assertEqual(len([result for result in results._result_cache if result is not None]), 0) self.assertEqual([int(result.pk) for result in results._result_cache if result is not None], []) 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]), 9) self.assertEqual([int(result.pk) for result in results._result_cache if result is not None], [1, 2, 3, 4, 5, 6, 7, 8, 10]) self.assertEqual(len(connections['default'].queries), 2) results._fill_cache(10, 20) self.assertEqual(len([result for result in results._result_cache if result is not None]), 17) self.assertEqual([int(result.pk) for result in results._result_cache if result is not None], [1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 15, 16, 17, 18, 19, 20]) self.assertEqual(len(connections['default'].queries), 4) results._fill_cache(20, 30) self.assertEqual(len([result for result in results._result_cache if result is not None]), 20) self.assertEqual([int(result.pk) for result in results._result_cache if result is not None], [1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 15, 16, 17, 18, 19, 20, 21, 22, 23]) self.assertEqual(len(connections['default'].queries), 6) def test_cache_is_full(self): reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) self.assertEqual(self.msqs._cache_is_full(), False) results = self.msqs.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_all(self): sqs = self.msqs.all() self.assertTrue(isinstance(sqs, SearchQuerySet)) def test_filter(self): sqs = self.msqs.filter(content='foo') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.query_filter), 1) def test_exclude(self): sqs = self.msqs.exclude(content='foo') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.query_filter), 1) def test_order_by(self): sqs = self.msqs.order_by('foo') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertTrue('foo' in sqs.query.order_by) def test_models(self): # Stow. old_unified_index = connections['default']._index ui = UnifiedIndex() bmmsi = BasicMockModelSearchIndex() bammsi = BasicAnotherMockModelSearchIndex() ui.build(indexes=[bmmsi, bammsi]) connections['default']._index = ui msqs = SearchQuerySet() sqs = msqs.all() self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.models), 0) sqs = msqs.models(MockModel) self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.models), 1) sqs = msqs.models(MockModel, AnotherMockModel) self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.models), 2) # This will produce a warning. ui.build(indexes=[bmmsi]) sqs = msqs.models(AnotherMockModel) self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.models), 1) def test_result_class(self): sqs = self.msqs.all() self.assertTrue(issubclass(sqs.query.result_class, SearchResult)) # Custom class. class IttyBittyResult(object): pass sqs = self.msqs.result_class(IttyBittyResult) self.assertTrue(issubclass(sqs.query.result_class, IttyBittyResult)) # Reset to default. sqs = self.msqs.result_class(None) self.assertTrue(issubclass(sqs.query.result_class, SearchResult)) def test_boost(self): sqs = self.msqs.boost('foo', 10) self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.boost.keys()), 1) def test_highlight(self): sqs = self.msqs.highlight() self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(sqs.query.highlight, True) def test_spelling(self): # Test the case where spelling support is disabled. sqs = self.msqs.filter(content='Indx') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(sqs.spelling_suggestion(), None) self.assertEqual(sqs.spelling_suggestion('indexy'), None) def test_raw_search(self): self.assertEqual(len(self.msqs.raw_search('foo')), 23) self.assertEqual(len(self.msqs.raw_search('(content__exact:hello AND content__exact:world)')), 23) def test_load_all(self): # Models with character primary keys. sqs = SearchQuerySet() sqs.query.backend = CharPKMockSearchBackend('charpk') results = sqs.load_all().all() self.assertEqual(len(results._result_cache), 0) results._fill_cache(0, 2) self.assertEqual(len([result for result in results._result_cache if result is not None]), 2) # If nothing is handled, you get nothing. old_ui = connections['default']._index ui = UnifiedIndex() ui.build(indexes=[]) connections['default']._index = ui sqs = self.msqs.load_all() self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs), 0) connections['default']._index = old_ui # For full tests, see the solr_backend. def test_load_all_read_queryset(self): # Stow. old_ui = connections['default']._index ui = UnifiedIndex() gafmmsi = GhettoAFifthMockModelSearchIndex() ui.build(indexes=[gafmmsi]) connections['default']._index = ui gafmmsi.update() sqs = SearchQuerySet() results = sqs.load_all().all() results.query.backend = ReadQuerySetMockSearchBackend('default') results._fill_cache(0, 2) # The deleted result isn't returned self.assertEqual(len([result for result in results._result_cache if result is not None]), 1) # Register a SearchIndex with a read_queryset that returns deleted items rqstsi = TextReadQuerySetTestSearchIndex() ui.build(indexes=[rqstsi]) rqstsi.update() sqs = SearchQuerySet() results = sqs.load_all().all() results.query.backend = ReadQuerySetMockSearchBackend('default') results._fill_cache(0, 2) # Both the deleted and not deleted items are returned self.assertEqual(len([result for result in results._result_cache if result is not None]), 2) # Restore. connections['default']._index = old_ui def test_auto_query(self): sqs = self.msqs.auto_query('test search -stuff') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(repr(sqs.query.query_filter), '<SQ: AND content__contains=test search -stuff>') sqs = self.msqs.auto_query('test "my thing" search -stuff') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(repr(sqs.query.query_filter), '<SQ: AND content__contains=test "my thing" search -stuff>') sqs = self.msqs.auto_query('test "my thing" search \'moar quotes\' -stuff') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(repr(sqs.query.query_filter), '<SQ: AND content__contains=test "my thing" search \'moar quotes\' -stuff>') sqs = self.msqs.auto_query('test "my thing" search \'moar quotes\' "foo -stuff') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(repr(sqs.query.query_filter), '<SQ: AND content__contains=test "my thing" search \'moar quotes\' "foo -stuff>') sqs = self.msqs.auto_query('test - stuff') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(repr(sqs.query.query_filter), "<SQ: AND content__contains=test - stuff>") # Ensure bits in exact matches get escaped properly as well. sqs = self.msqs.auto_query('"pants:rule"') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(repr(sqs.query.query_filter), '<SQ: AND content__contains="pants:rule">') # Now with a different fieldname sqs = self.msqs.auto_query('test search -stuff', fieldname='title') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(repr(sqs.query.query_filter), "<SQ: AND title__contains=test search -stuff>") sqs = self.msqs.auto_query('test "my thing" search -stuff', fieldname='title') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(repr(sqs.query.query_filter), '<SQ: AND title__contains=test "my thing" search -stuff>') def test_count(self): self.assertEqual(self.msqs.count(), 23) def test_facet_counts(self): self.assertEqual(self.msqs.facet_counts(), {}) def test_best_match(self): self.assertTrue(isinstance(self.msqs.best_match(), SearchResult)) def test_latest(self): self.assertTrue(isinstance(self.msqs.latest('pub_date'), SearchResult)) def test_more_like_this(self): mock = MockModel() mock.id = 1 self.assertEqual(len(self.msqs.more_like_this(mock)), 23) def test_facets(self): sqs = self.msqs.facet('foo') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.facets), 1) sqs2 = self.msqs.facet('foo').facet('bar') self.assertTrue(isinstance(sqs2, SearchQuerySet)) self.assertEqual(len(sqs2.query.facets), 2) def test_date_facets(self): try: sqs = self.msqs.date_facet('foo', start_date=datetime.date(2008, 2, 25), end_date=datetime.date(2009, 2, 25), gap_by='smarblaph') self.fail() except FacetingError, e: self.assertEqual(str(e), "The gap_by ('smarblaph') must be one of the following: year, month, day, hour, minute, second.") sqs = self.msqs.date_facet('foo', start_date=datetime.date(2008, 2, 25), end_date=datetime.date(2009, 2, 25), gap_by='month') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.date_facets), 1) sqs2 = self.msqs.date_facet('foo', start_date=datetime.date(2008, 2, 25), end_date=datetime.date(2009, 2, 25), gap_by='month').date_facet('bar', start_date=datetime.date(2007, 2, 25), end_date=datetime.date(2009, 2, 25), gap_by='year') self.assertTrue(isinstance(sqs2, SearchQuerySet)) self.assertEqual(len(sqs2.query.date_facets), 2)
class SearchQuerySetTestCase(TestCase): fixtures = ['base_data.json', 'bulk_data.json'] def setUp(self): super(SearchQuerySetTestCase, self).setUp() # Stow. self.old_unified_index = connections['default']._index self.ui = UnifiedIndex() self.bmmsi = BasicMockModelSearchIndex() self.cpkmmsi = CharPKMockModelSearchIndex() self.uuidmmsi = SimpleMockUUIDModelIndex() self.ui.build(indexes=[self.bmmsi, self.cpkmmsi, self.uuidmmsi]) connections['default']._index = self.ui # Update the "index". backend = connections['default'].get_backend() backend.clear() backend.update(self.bmmsi, MockModel.objects.all()) self.msqs = SearchQuerySet() # Stow. reset_search_queries() def tearDown(self): # Restore. connections['default']._index = self.old_unified_index super(SearchQuerySetTestCase, self).tearDown() def test_len(self): self.assertEqual(len(self.msqs), 23) def test_repr(self): reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) self.assertRegexpMatches(repr(self.msqs), r'^<SearchQuerySet: query=<test_haystack.mocks.MockSearchQuery object' r' at 0x[0-9A-Fa-f]+>, using=None>$') def test_iter(self): reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) msqs = self.msqs.all() results = [int(res.pk) for res in iter(msqs)] self.assertEqual(results, [res.pk for res in MOCK_SEARCH_RESULTS[:23]]) self.assertEqual(len(connections['default'].queries), 3) def test_slice(self): reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) results = self.msqs.all() self.assertEqual([int(res.pk) for res in results[1:11]], [res.pk for res in MOCK_SEARCH_RESULTS[1:11]]) self.assertEqual(len(connections['default'].queries), 1) reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) results = self.msqs.all() self.assertEqual(int(results[22].pk), MOCK_SEARCH_RESULTS[22].pk) self.assertEqual(len(connections['default'].queries), 1) def test_manual_iter(self): results = self.msqs.all() reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) check = [result.pk for result in results._manual_iter()] self.assertEqual(check, [u'1', u'2', u'3', u'4', u'5', u'6', u'7', u'8', u'9', u'10', u'11', u'12', u'13', u'14', u'15', u'16', u'17', u'18', u'19', u'20', u'21', u'22', u'23']) self.assertEqual(len(connections['default'].queries), 3) reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) # Test to ensure we properly fill the cache, even if we get fewer # results back (not a handled model) than the hit count indicates. # This will hang indefinitely if broken. # CharPK testing old_ui = self.ui self.ui.build(indexes=[self.cpkmmsi]) connections['default']._index = self.ui self.cpkmmsi.update() results = self.msqs.all() loaded = [result.pk for result in results._manual_iter()] self.assertEqual(loaded, [u'sometext', u'1234']) self.assertEqual(len(connections['default'].queries), 1) #UUID testing self.ui.build(indexes=[self.uuidmmsi]) connections['default']._index = self.ui self.uuidmmsi.update() results = self.msqs.all() loaded = [result.pk for result in results._manual_iter()] self.assertEqual(loaded, [u'53554c58-7051-4350-bcc9-dad75eb248a9', u'77554c58-7051-4350-bcc9-dad75eb24888']) connections['default']._index = old_ui def test_cache_is_full(self): reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) self.assertEqual(self.msqs._cache_is_full(), False) results = self.msqs.all() fire_the_iterator_and_fill_cache = list(results) self.assertEqual(23, len(fire_the_iterator_and_fill_cache)) self.assertEqual(results._cache_is_full(), True) self.assertEqual(len(connections['default'].queries), 4) def test_all(self): sqs = self.msqs.all() self.assertTrue(isinstance(sqs, SearchQuerySet)) def test_filter(self): sqs = self.msqs.filter(content='foo') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.query_filter), 1) def test_exclude(self): sqs = self.msqs.exclude(content='foo') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.query_filter), 1) def test_order_by(self): sqs = self.msqs.order_by('foo') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertTrue('foo' in sqs.query.order_by) def test_models(self): # Stow. old_unified_index = connections['default']._index ui = UnifiedIndex() bmmsi = BasicMockModelSearchIndex() bammsi = BasicAnotherMockModelSearchIndex() ui.build(indexes=[bmmsi, bammsi]) connections['default']._index = ui msqs = SearchQuerySet() sqs = msqs.all() self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.models), 0) sqs = msqs.models(MockModel) self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.models), 1) sqs = msqs.models(MockModel, AnotherMockModel) self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.models), 2) # This will produce a warning. ui.build(indexes=[bmmsi]) sqs = msqs.models(AnotherMockModel) self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.models), 1) def test_result_class(self): sqs = self.msqs.all() self.assertTrue(issubclass(sqs.query.result_class, SearchResult)) # Custom class. class IttyBittyResult(object): pass sqs = self.msqs.result_class(IttyBittyResult) self.assertTrue(issubclass(sqs.query.result_class, IttyBittyResult)) # Reset to default. sqs = self.msqs.result_class(None) self.assertTrue(issubclass(sqs.query.result_class, SearchResult)) def test_boost(self): sqs = self.msqs.boost('foo', 10) self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.boost.keys()), 1) def test_highlight(self): sqs = self.msqs.highlight() self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(sqs.query.highlight, True) def test_spelling_override(self): sqs = self.msqs.filter(content='not the spellchecking query') self.assertEqual(sqs.query.spelling_query, None) sqs = self.msqs.set_spelling_query('override') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(sqs.query.spelling_query, 'override') def test_spelling_suggestions(self): # Test the case where spelling support is disabled. sqs = self.msqs.filter(content='Indx') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(sqs.spelling_suggestion(), None) self.assertEqual(sqs.spelling_suggestion('indexy'), None) def test_raw_search(self): self.assertEqual(len(self.msqs.raw_search('foo')), 23) self.assertEqual(len(self.msqs.raw_search('(content__exact:hello AND content__exact:world)')), 23) def test_load_all(self): # Models with character primary keys. sqs = SearchQuerySet() sqs.query.backend = CharPKMockSearchBackend('charpk') results = sqs.load_all().all() self.assertEqual(len(results._result_cache), 0) results._fill_cache(0, 2) self.assertEqual(len([result for result in results._result_cache if result is not None]), 2) # Models with uuid primary keys. sqs = SearchQuerySet() sqs.query.backend = UUIDMockSearchBackend('uuid') results = sqs.load_all().all() self.assertEqual(len(results._result_cache), 0) results._fill_cache(0, 2) self.assertEqual(len([result for result in results._result_cache if result is not None]), 2) # If nothing is handled, you get nothing. old_ui = connections['default']._index ui = UnifiedIndex() ui.build(indexes=[]) connections['default']._index = ui sqs = self.msqs.load_all() self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs), 0) connections['default']._index = old_ui # For full tests, see the solr_backend. def test_load_all_read_queryset(self): # Stow. old_ui = connections['default']._index ui = UnifiedIndex() gafmmsi = GhettoAFifthMockModelSearchIndex() ui.build(indexes=[gafmmsi]) connections['default']._index = ui gafmmsi.update() sqs = SearchQuerySet() results = sqs.load_all().all() results.query.backend = ReadQuerySetMockSearchBackend('default') results._fill_cache(0, 2) # The deleted result isn't returned self.assertEqual(len([result for result in results._result_cache if result is not None]), 1) # Register a SearchIndex with a read_queryset that returns deleted items rqstsi = TextReadQuerySetTestSearchIndex() ui.build(indexes=[rqstsi]) rqstsi.update() sqs = SearchQuerySet() results = sqs.load_all().all() results.query.backend = ReadQuerySetMockSearchBackend('default') results._fill_cache(0, 2) # Both the deleted and not deleted items are returned self.assertEqual(len([result for result in results._result_cache if result is not None]), 2) # Restore. connections['default']._index = old_ui def test_auto_query(self): sqs = self.msqs.auto_query('test search -stuff') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(repr(sqs.query.query_filter), '<SQ: AND content__content=test search -stuff>') sqs = self.msqs.auto_query('test "my thing" search -stuff') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(repr(sqs.query.query_filter), '<SQ: AND content__content=test "my thing" search -stuff>') sqs = self.msqs.auto_query('test "my thing" search \'moar quotes\' -stuff') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(repr(sqs.query.query_filter), '<SQ: AND content__content=test "my thing" search \'moar quotes\' -stuff>') sqs = self.msqs.auto_query('test "my thing" search \'moar quotes\' "foo -stuff') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(repr(sqs.query.query_filter), '<SQ: AND content__content=test "my thing" search \'moar quotes\' "foo -stuff>') sqs = self.msqs.auto_query('test - stuff') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(repr(sqs.query.query_filter), "<SQ: AND content__content=test - stuff>") # Ensure bits in exact matches get escaped properly as well. sqs = self.msqs.auto_query('"pants:rule"') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(repr(sqs.query.query_filter), '<SQ: AND content__content="pants:rule">') # Now with a different fieldname sqs = self.msqs.auto_query('test search -stuff', fieldname='title') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(repr(sqs.query.query_filter), "<SQ: AND title__content=test search -stuff>") sqs = self.msqs.auto_query('test "my thing" search -stuff', fieldname='title') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(repr(sqs.query.query_filter), '<SQ: AND title__content=test "my thing" search -stuff>') def test_count(self): self.assertEqual(self.msqs.count(), 23) def test_facet_counts(self): self.assertEqual(self.msqs.facet_counts(), {}) def test_best_match(self): self.assertTrue(isinstance(self.msqs.best_match(), SearchResult)) def test_latest(self): self.assertTrue(isinstance(self.msqs.latest('pub_date'), SearchResult)) def test_more_like_this(self): mock = MockModel() mock.id = 1 self.assertEqual(len(self.msqs.more_like_this(mock)), 23) def test_facets(self): sqs = self.msqs.facet('foo') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.facets), 1) sqs2 = self.msqs.facet('foo').facet('bar') self.assertTrue(isinstance(sqs2, SearchQuerySet)) self.assertEqual(len(sqs2.query.facets), 2) def test_date_facets(self): try: sqs = self.msqs.date_facet('foo', start_date=datetime.date(2008, 2, 25), end_date=datetime.date(2009, 2, 25), gap_by='smarblaph') self.fail() except FacetingError as e: self.assertEqual(str(e), "The gap_by ('smarblaph') must be one of the following: year, month, day, hour, minute, second.") sqs = self.msqs.date_facet('foo', start_date=datetime.date(2008, 2, 25), end_date=datetime.date(2009, 2, 25), gap_by='month') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.date_facets), 1) sqs2 = self.msqs.date_facet('foo', start_date=datetime.date(2008, 2, 25), end_date=datetime.date(2009, 2, 25), gap_by='month').date_facet('bar', start_date=datetime.date(2007, 2, 25), end_date=datetime.date(2009, 2, 25), gap_by='year') self.assertTrue(isinstance(sqs2, SearchQuerySet)) self.assertEqual(len(sqs2.query.date_facets), 2) def test_query_facets(self): sqs = self.msqs.query_facet('foo', '[bar TO *]') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.query_facets), 1) sqs2 = self.msqs.query_facet('foo', '[bar TO *]').query_facet('bar', '[100 TO 499]') self.assertTrue(isinstance(sqs2, SearchQuerySet)) self.assertEqual(len(sqs2.query.query_facets), 2) # Test multiple query facets on a single field sqs3 = self.msqs.query_facet('foo', '[bar TO *]').query_facet('bar', '[100 TO 499]').query_facet('foo', '[1000 TO 1499]') self.assertTrue(isinstance(sqs3, SearchQuerySet)) self.assertEqual(len(sqs3.query.query_facets), 3) def test_stats(self): sqs = self.msqs.stats_facet('foo', 'bar') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.stats), 1) sqs2 = self.msqs.stats_facet('foo', 'bar').stats_facet('foo', 'baz') self.assertTrue(isinstance(sqs2, SearchQuerySet)) self.assertEqual(len(sqs2.query.stats), 1) sqs3 = self.msqs.stats_facet('foo', 'bar').stats_facet('moof', 'baz') self.assertTrue(isinstance(sqs3, SearchQuerySet)) self.assertEqual(len(sqs3.query.stats), 2) def test_narrow(self): sqs = self.msqs.narrow('foo:moof') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.narrow_queries), 1) def test_clone(self): results = self.msqs.filter(foo='bar', foo__lt='10') clone = results._clone() self.assertTrue(isinstance(clone, SearchQuerySet)) self.assertEqual(str(clone.query), str(results.query)) self.assertEqual(clone._result_cache, []) self.assertEqual(clone._result_count, None) self.assertEqual(clone._cache_full, False) self.assertEqual(clone._using, results._using) def test_using(self): sqs = SearchQuerySet(using='default') self.assertNotEqual(sqs.query, None) self.assertEqual(sqs.query._using, 'default') def test_chaining(self): sqs = self.msqs.filter(content='foo') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.query_filter), 1) # A second instance should inherit none of the changes from above. sqs = self.msqs.filter(content='bar') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.query_filter), 1) def test_none(self): sqs = self.msqs.none() self.assertTrue(isinstance(sqs, EmptySearchQuerySet)) self.assertEqual(len(sqs), 0) def test___and__(self): sqs1 = self.msqs.filter(content='foo') sqs2 = self.msqs.filter(content='bar') sqs = sqs1 & sqs2 self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.query_filter), 2) def test___or__(self): sqs1 = self.msqs.filter(content='foo') sqs2 = self.msqs.filter(content='bar') sqs = sqs1 | sqs2 self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.query_filter), 2) def test_and_or(self): """ Combining AND queries with OR should give AND(OR(a, b), OR(c, d)) """ sqs1 = self.msqs.filter(content='foo').filter(content='oof') sqs2 = self.msqs.filter(content='bar').filter(content='rab') sqs = sqs1 | sqs2 self.assertEqual(sqs.query.query_filter.connector, 'OR') self.assertEqual(repr(sqs.query.query_filter.children[0]), repr(sqs1.query.query_filter)) self.assertEqual(repr(sqs.query.query_filter.children[1]), repr(sqs2.query.query_filter)) def test_or_and(self): """ Combining OR queries with AND should give OR(AND(a, b), AND(c, d)) """ sqs1 = self.msqs.filter(content='foo').filter_or(content='oof') sqs2 = self.msqs.filter(content='bar').filter_or(content='rab') sqs = sqs1 & sqs2 self.assertEqual(sqs.query.query_filter.connector, 'AND') self.assertEqual(repr(sqs.query.query_filter.children[0]), repr(sqs1.query.query_filter)) self.assertEqual(repr(sqs.query.query_filter.children[1]), repr(sqs2.query.query_filter))
class LiveWhooshMoreLikeThisTestCase(TestCase): fixtures = ['bulk_data.json'] def setUp(self): super(LiveWhooshMoreLikeThisTestCase, self).setUp() # Stow. temp_path = os.path.join('tmp', 'test_whoosh_query') self.old_whoosh_path = getattr(settings, 'HAYSTACK_WHOOSH_PATH', temp_path) settings.HAYSTACK_WHOOSH_PATH = temp_path self.site = SearchSite() self.sb = SearchBackend(site=self.site) self.wmsi = WhooshMockSearchIndex(MockModel, backend=self.sb) self.site.register(MockModel, WhooshMockSearchIndex) # Stow. import haystack self.old_debug = settings.DEBUG settings.DEBUG = True self.old_site = haystack.site haystack.site = self.site self.sb.setup() self.sqs = SearchQuerySet(site=self.site) # Wipe it clean. self.sqs.query.backend.clear() for mock in MockModel.objects.all(): self.wmsi.update_object(mock) def tearDown(self): if os.path.exists(settings.HAYSTACK_WHOOSH_PATH): shutil.rmtree(settings.HAYSTACK_WHOOSH_PATH) settings.HAYSTACK_WHOOSH_PATH = self.old_whoosh_path import haystack haystack.site = self.old_site settings.DEBUG = self.old_debug super(LiveWhooshMoreLikeThisTestCase, self).tearDown() def tearDown(self): # Restore. import haystack haystack.site = self.old_site super(LiveWhooshMoreLikeThisTestCase, self).tearDown() def test_more_like_this(self): mlt = self.sqs.more_like_this(MockModel.objects.get(pk=22)) self.assertEqual(mlt.count(), 22) self.assertEqual([result.pk for result in mlt], [u'9', u'8', u'7', u'6', u'5', u'4', u'3', u'2', u'1', u'21', u'20', u'19', u'18', u'17', u'16', u'15', u'14', u'13', u'12', u'11', u'10', u'23']) self.assertEqual(len([result.pk for result in mlt]), 22) alt_mlt = self.sqs.filter(name='daniel3').more_like_this(MockModel.objects.get(pk=13)) self.assertEqual(alt_mlt.count(), 8) self.assertEqual([result.pk for result in alt_mlt], [u'4', u'3', u'22', u'19', u'17', u'16', u'10', u'23']) self.assertEqual(len([result.pk for result in alt_mlt]), 8) alt_mlt_with_models = self.sqs.models(MockModel).more_like_this(MockModel.objects.get(pk=11)) self.assertEqual(alt_mlt_with_models.count(), 22) self.assertEqual([result.pk for result in alt_mlt_with_models], [u'9', u'8', u'7', u'6', u'5', u'4', u'3', u'2', u'1', u'22', u'21', u'20', u'19', u'18', u'17', u'16', u'15', u'14', u'13', u'12', u'10', u'23']) self.assertEqual(len([result.pk for result in alt_mlt_with_models]), 22) if hasattr(MockModel.objects, 'defer'): # Make sure MLT works with deferred bits. mi = MockModel.objects.defer('foo').get(pk=21) self.assertEqual(mi._deferred, True) deferred = self.sqs.models(MockModel).more_like_this(mi) self.assertEqual(deferred.count(), 0) self.assertEqual([result.pk for result in deferred], []) self.assertEqual(len([result.pk for result in deferred]), 0) # Ensure that swapping the ``result_class`` works. self.assertTrue(isinstance(self.sqs.result_class(MockSearchResult).more_like_this(MockModel.objects.get(pk=21))[0], MockSearchResult))
class LiveWhooshMoreLikeThisTestCase(TestCase): fixtures = ['bulk_data.json'] def setUp(self): super(LiveWhooshMoreLikeThisTestCase, self).setUp() # Stow. temp_path = os.path.join('tmp', 'test_whoosh_query') self.old_whoosh_path = settings.HAYSTACK_CONNECTIONS['default']['PATH'] settings.HAYSTACK_CONNECTIONS['default']['PATH'] = temp_path self.old_ui = connections['default'].get_unified_index() self.ui = UnifiedIndex() self.wmmi = WhooshMockSearchIndex() self.wamsi = WhooshAnotherMockSearchIndex() self.ui.build(indexes=[self.wmmi, self.wamsi]) self.sb = connections['default'].get_backend() connections['default']._index = self.ui self.sb.setup() self.raw_whoosh = self.sb.index self.parser = QueryParser(self.sb.content_field_name, schema=self.sb.schema) self.sb.delete_index() self.wmmi.update() self.wamsi.update() self.sqs = SearchQuerySet() def tearDown(self): if os.path.exists(settings.HAYSTACK_CONNECTIONS['default']['PATH']): shutil.rmtree(settings.HAYSTACK_CONNECTIONS['default']['PATH']) settings.HAYSTACK_CONNECTIONS['default']['PATH'] = self.old_whoosh_path connections['default']._index = self.old_ui super(LiveWhooshMoreLikeThisTestCase, self).tearDown() def test_more_like_this(self): mlt = self.sqs.more_like_this(MockModel.objects.get(pk=22)) self.assertEqual(mlt.count(), 22) self.assertEqual(sorted([result.pk for result in mlt]), sorted([u'9', u'8', u'7', u'6', u'5', u'4', u'3', u'2', u'1', u'21', u'20', u'19', u'18', u'17', u'16', u'15', u'14', u'13', u'12', u'11', u'10', u'23'])) self.assertEqual(len([result.pk for result in mlt]), 22) alt_mlt = self.sqs.filter(name='daniel3').more_like_this(MockModel.objects.get(pk=13)) self.assertEqual(alt_mlt.count(), 8) self.assertEqual(sorted([result.pk for result in alt_mlt]), sorted([u'4', u'3', u'22', u'19', u'17', u'16', u'10', u'23'])) self.assertEqual(len([result.pk for result in alt_mlt]), 8) alt_mlt_with_models = self.sqs.models(MockModel).more_like_this(MockModel.objects.get(pk=11)) self.assertEqual(alt_mlt_with_models.count(), 22) self.assertEqual(sorted([result.pk for result in alt_mlt_with_models]), sorted([u'9', u'8', u'7', u'6', u'5', u'4', u'3', u'2', u'1', u'22', u'21', u'20', u'19', u'18', u'17', u'16', u'15', u'14', u'13', u'12', u'10', u'23'])) self.assertEqual(len([result.pk for result in alt_mlt_with_models]), 22) if hasattr(MockModel.objects, 'defer'): # Make sure MLT works with deferred bits. mi = MockModel.objects.defer('foo').get(pk=21) self.assertEqual(mi._deferred, True) deferred = self.sqs.models(MockModel).more_like_this(mi) self.assertEqual(deferred.count(), 0) self.assertEqual([result.pk for result in deferred], []) self.assertEqual(len([result.pk for result in deferred]), 0) # Ensure that swapping the ``result_class`` works. self.assertTrue(isinstance(self.sqs.result_class(MockSearchResult).more_like_this(MockModel.objects.get(pk=21))[0], MockSearchResult))
class LiveSolrMoreLikeThisTestCase(TestCase): fixtures = ['bulk_data.json'] def setUp(self): super(LiveSolrMoreLikeThisTestCase, self).setUp() # Wipe it clean. clear_solr_index() # With the models registered, you get the proper bits. import haystack from haystack.sites import SearchSite # Stow. self.old_site = haystack.site test_site = SearchSite() test_site.register(MockModel, SolrMockModelSearchIndex) test_site.register(AnotherMockModel, SolrAnotherMockModelSearchIndex) haystack.site = test_site self.sqs = SearchQuerySet() test_site.get_index(MockModel).update() test_site.get_index(AnotherMockModel).update() def tearDown(self): # Restore. import haystack haystack.site = self.old_site super(LiveSolrMoreLikeThisTestCase, self).tearDown() def test_more_like_this(self): mlt = self.sqs.more_like_this(MockModel.objects.get(pk=1)) self.assertEqual(mlt.count(), 24) self.assertEqual([result.pk for result in mlt], [ '6', '14', '4', '10', '22', '5', '3', '12', '2', '23', '18', '19', '13', '7', '15', '21', '9', '1', '2', '20', '16', '17', '8', '11' ]) self.assertEqual(len([result.pk for result in mlt]), 24) alt_mlt = self.sqs.filter(name='daniel3').more_like_this( MockModel.objects.get(pk=3)) self.assertEqual(alt_mlt.count(), 10) self.assertEqual( [result.pk for result in alt_mlt], ['23', '13', '17', '16', '22', '19', '4', '10', '1', '2']) self.assertEqual(len([result.pk for result in alt_mlt]), 10) alt_mlt_with_models = self.sqs.models(MockModel).more_like_this( MockModel.objects.get(pk=1)) self.assertEqual(alt_mlt_with_models.count(), 22) self.assertEqual([result.pk for result in alt_mlt_with_models], [ '6', '14', '4', '10', '22', '5', '3', '12', '2', '23', '18', '19', '13', '7', '15', '21', '9', '20', '16', '17', '8', '11' ]) self.assertEqual(len([result.pk for result in alt_mlt_with_models]), 22) if hasattr(MockModel.objects, 'defer'): # Make sure MLT works with deferred bits. mi = MockModel.objects.defer('foo').get(pk=1) self.assertEqual(mi._deferred, True) deferred = self.sqs.models(MockModel).more_like_this(mi) self.assertEqual(deferred.count(), 0) self.assertEqual([result.pk for result in deferred], []) self.assertEqual(len([result.pk for result in deferred]), 0) # Ensure that swapping the ``result_class`` works. self.assertTrue( isinstance( self.sqs.result_class(MockSearchResult).more_like_this( MockModel.objects.get(pk=1))[0], MockSearchResult))
class LiveSolrMoreLikeThisTestCase(TestCase): fixtures = ['bulk_data.json'] def setUp(self): super(LiveSolrMoreLikeThisTestCase, self).setUp() # Wipe it clean. clear_solr_index() self.old_ui = connections['default'].get_unified_index() self.ui = UnifiedIndex() self.smmi = SolrMockModelSearchIndex() self.sammi = SolrAnotherMockModelSearchIndex() self.ui.build(indexes=[self.smmi, self.sammi]) connections['default']._index = self.ui self.sqs = SearchQuerySet() self.smmi.update() self.sammi.update() def tearDown(self): # Restore. connections['default']._index = self.old_ui super(LiveSolrMoreLikeThisTestCase, self).tearDown() def test_more_like_this(self): mlt = self.sqs.more_like_this(MockModel.objects.get(pk=1)) self.assertEqual(mlt.count(), 24) self.assertEqual(sorted([result.pk for result in mlt]), sorted(['6', '14', '4', '10', '22', '5', '3', '12', '2', '23', '18', '19', '13', '7', '15', '21', '9', '1', '2', '20', '16', '17', '8', '11'])) self.assertEqual(len([result.pk for result in mlt]), 24) alt_mlt = self.sqs.filter(name='daniel3').more_like_this(MockModel.objects.get(pk=3)) self.assertEqual(alt_mlt.count(), 10) self.assertEqual(sorted([result.pk for result in alt_mlt]), sorted(['23', '13', '17', '16', '22', '19', '4', '10', '1', '2'])) self.assertEqual(len([result.pk for result in alt_mlt]), 10) alt_mlt_with_models = self.sqs.models(MockModel).more_like_this(MockModel.objects.get(pk=1)) self.assertEqual(alt_mlt_with_models.count(), 22) self.assertEqual(sorted([result.pk for result in alt_mlt_with_models]), sorted(['6', '14', '4', '10', '22', '5', '3', '12', '2', '23', '18', '19', '13', '7', '15', '21', '9', '20', '16', '17', '8', '11'])) self.assertEqual(len([result.pk for result in alt_mlt_with_models]), 22) if hasattr(MockModel.objects, 'defer'): # Make sure MLT works with deferred bits. mi = MockModel.objects.defer('foo').get(pk=1) self.assertEqual(mi._deferred, True) deferred = self.sqs.models(MockModel).more_like_this(mi) self.assertEqual(deferred.count(), 0) self.assertEqual([result.pk for result in deferred], []) self.assertEqual(len([result.pk for result in deferred]), 0) # Ensure that swapping the ``result_class`` works. self.assertTrue(isinstance(self.sqs.result_class(MockSearchResult).more_like_this(MockModel.objects.get(pk=1))[0], MockSearchResult))
class LiveSolrMoreLikeThisTestCase(TestCase): fixtures = ['bulk_data.json'] def setUp(self): super(LiveSolrMoreLikeThisTestCase, self).setUp() # Wipe it clean. clear_solr_index() self.old_ui = connections['default'].get_unified_index() self.ui = UnifiedIndex() self.smmi = SolrMockModelSearchIndex() self.sammi = SolrAnotherMockModelSearchIndex() self.ui.build(indexes=[self.smmi, self.sammi]) connections['default']._index = self.ui self.sqs = SearchQuerySet() self.smmi.update() self.sammi.update() def tearDown(self): # Restore. connections['default']._index = self.old_ui super(LiveSolrMoreLikeThisTestCase, self).tearDown() def test_more_like_this(self): all_mlt = self.sqs.more_like_this(MockModel.objects.get(pk=1)) self.assertEqual(all_mlt.count(), len([result.pk for result in all_mlt]), msg="mlt SearchQuerySet .count() didn't match retrieved result length") # Rather than hard-code assumptions about Solr's return order, we have a few very similar # items which we'll confirm are included in the first 5 results. This is still ugly as we're # hard-coding primary keys but it's better than breaking any time a Solr update or data # change causes a score to shift slightly top_results = [int(result.pk) for result in all_mlt[:5]] for i in (14, 6, 4, 22, 10): self.assertIn(i, top_results) filtered_mlt = self.sqs.filter(name='daniel3').more_like_this(MockModel.objects.get(pk=3)) self.assertLess(filtered_mlt.count(), all_mlt.count()) top_filtered_results = [int(result.pk) for result in filtered_mlt[:5]] for i in (23, 13, 17, 16, 19): self.assertIn(i, top_filtered_results) filtered_mlt_with_models = self.sqs.models(MockModel).more_like_this(MockModel.objects.get(pk=1)) self.assertLessEqual(filtered_mlt_with_models.count(), all_mlt.count()) top_filtered_with_models = [int(result.pk) for result in filtered_mlt_with_models[:5]] for i in (14, 6, 4, 22, 10): self.assertIn(i, top_filtered_with_models) def test_more_like_this_defer(self): mi = MockModel.objects.defer('foo').get(pk=1) # FIXME: this currently is known to fail because haystack.utils.loading doesn't see the # MockModel_Deferred_foo class as registered: deferred = self.sqs.models(MockModel).more_like_this(mi) self.assertEqual(deferred.count(), 0) self.assertEqual([result.pk for result in deferred], []) self.assertEqual(len([result.pk for result in deferred]), 0) def test_more_like_this_custom_result_class(self): """Ensure that swapping the ``result_class`` works""" first_result = self.sqs.result_class(MockSearchResult).more_like_this(MockModel.objects.get(pk=1))[0] self.assertIsInstance(first_result, MockSearchResult)
class LiveSolrMoreLikeThisTestCase(TestCase): fixtures = ["bulk_data.json"] def setUp(self): super(LiveSolrMoreLikeThisTestCase, self).setUp() # Wipe it clean. clear_solr_index() # With the models registered, you get the proper bits. import haystack from haystack.sites import SearchSite # Stow. self.old_site = haystack.site test_site = SearchSite() test_site.register(MockModel, SolrMockModelSearchIndex) test_site.register(AnotherMockModel, SolrAnotherMockModelSearchIndex) haystack.site = test_site self.sqs = SearchQuerySet() test_site.get_index(MockModel).update() test_site.get_index(AnotherMockModel).update() def tearDown(self): # Restore. import haystack haystack.site = self.old_site super(LiveSolrMoreLikeThisTestCase, self).tearDown() def test_more_like_this(self): mlt = self.sqs.more_like_this(MockModel.objects.get(pk=1)) self.assertEqual(mlt.count(), 24) self.assertEqual( [result.pk for result in mlt], [ "6", "14", "4", "10", "22", "5", "3", "12", "2", "23", "18", "19", "13", "7", "15", "21", "9", "1", "2", "20", "16", "17", "8", "11", ], ) self.assertEqual(len([result.pk for result in mlt]), 24) alt_mlt = self.sqs.filter(name="daniel3").more_like_this(MockModel.objects.get(pk=3)) self.assertEqual(alt_mlt.count(), 10) self.assertEqual([result.pk for result in alt_mlt], ["23", "13", "17", "16", "22", "19", "4", "10", "1", "2"]) self.assertEqual(len([result.pk for result in alt_mlt]), 10) alt_mlt_with_models = self.sqs.models(MockModel).more_like_this(MockModel.objects.get(pk=1)) self.assertEqual(alt_mlt_with_models.count(), 22) self.assertEqual( [result.pk for result in alt_mlt_with_models], [ "6", "14", "4", "10", "22", "5", "3", "12", "2", "23", "18", "19", "13", "7", "15", "21", "9", "20", "16", "17", "8", "11", ], ) self.assertEqual(len([result.pk for result in alt_mlt_with_models]), 22) if hasattr(MockModel.objects, "defer"): # Make sure MLT works with deferred bits. mi = MockModel.objects.defer("foo").get(pk=1) self.assertEqual(mi._deferred, True) deferred = self.sqs.models(MockModel).more_like_this(mi) self.assertEqual(deferred.count(), 0) self.assertEqual([result.pk for result in deferred], []) self.assertEqual(len([result.pk for result in deferred]), 0) # Ensure that swapping the ``result_class`` works. self.assertTrue( isinstance( self.sqs.result_class(MockSearchResult).more_like_this(MockModel.objects.get(pk=1))[0], MockSearchResult ) )
class LiveWhooshMoreLikeThisTestCase(WhooshTestCase): fixtures = ["bulk_data.json"] def setUp(self): super().setUp() # Stow. self.old_ui = connections["whoosh"].get_unified_index() self.ui = UnifiedIndex() self.wmmi = WhooshMockSearchIndex() self.wamsi = WhooshAnotherMockSearchIndex() self.ui.build(indexes=[self.wmmi, self.wamsi]) self.sb = connections["whoosh"].get_backend() connections["whoosh"]._index = self.ui self.sb.setup() self.raw_whoosh = self.sb.index self.parser = QueryParser(self.sb.content_field_name, schema=self.sb.schema) self.sb.delete_index() self.wmmi.update() self.wamsi.update() self.sqs = SearchQuerySet("whoosh") def tearDown(self): connections["whoosh"]._index = self.old_ui super().tearDown() # We expect failure here because, despite not changing the code, Whoosh # 2.5.1 returns incorrect counts/results. Huzzah. @unittest.expectedFailure def test_more_like_this(self): mlt = self.sqs.more_like_this(MockModel.objects.get(pk=22)) self.assertEqual(mlt.count(), 22) self.assertEqual( sorted([result.pk for result in mlt]), sorted( [ "9", "8", "7", "6", "5", "4", "3", "2", "1", "21", "20", "19", "18", "17", "16", "15", "14", "13", "12", "11", "10", "23", ] ), ) self.assertEqual(len([result.pk for result in mlt]), 22) alt_mlt = self.sqs.filter(name="daniel3").more_like_this( MockModel.objects.get(pk=13) ) self.assertEqual(alt_mlt.count(), 8) self.assertEqual( sorted([result.pk for result in alt_mlt]), sorted(["4", "3", "22", "19", "17", "16", "10", "23"]), ) self.assertEqual(len([result.pk for result in alt_mlt]), 8) alt_mlt_with_models = self.sqs.models(MockModel).more_like_this( MockModel.objects.get(pk=11) ) self.assertEqual(alt_mlt_with_models.count(), 22) self.assertEqual( sorted([result.pk for result in alt_mlt_with_models]), sorted( [ "9", "8", "7", "6", "5", "4", "3", "2", "1", "22", "21", "20", "19", "18", "17", "16", "15", "14", "13", "12", "10", "23", ] ), ) self.assertEqual(len([result.pk for result in alt_mlt_with_models]), 22) if hasattr(MockModel.objects, "defer"): # Make sure MLT works with deferred bits. mi = MockModel.objects.defer("foo").get(pk=22) deferred = self.sqs.models(MockModel).more_like_this(mi) self.assertEqual(deferred.count(), 22) self.assertEqual( sorted([result.pk for result in deferred]), sorted( [ "9", "8", "7", "6", "5", "4", "3", "2", "1", "21", "20", "19", "18", "17", "16", "15", "14", "13", "12", "11", "10", "23", ] ), ) self.assertEqual(len([result.pk for result in deferred]), 22) # Ensure that swapping the ``result_class`` works. self.assertTrue( isinstance( self.sqs.result_class(MockSearchResult).more_like_this( MockModel.objects.get(pk=21) )[0], MockSearchResult, ) )
class SearchQuerySetTestCase(TestCase): fixtures = ['bulk_data.json'] def setUp(self): super(SearchQuerySetTestCase, self).setUp() # Stow. self.old_unified_index = connections['default']._index self.ui = UnifiedIndex() self.bmmsi = BasicMockModelSearchIndex() self.cpkmmsi = CharPKMockModelSearchIndex() self.ui.build(indexes=[self.bmmsi, self.cpkmmsi]) connections['default']._index = self.ui # Update the "index". backend = connections['default'].get_backend() backend.clear() backend.update(self.bmmsi, MockModel.objects.all()) self.msqs = SearchQuerySet() # Stow. reset_search_queries() def tearDown(self): # Restore. connections['default']._index = self.old_unified_index super(SearchQuerySetTestCase, self).tearDown() def test_len(self): self.assertEqual(len(self.msqs), 23) def test_repr(self): reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) self.assertRegexpMatches( repr(self.msqs), r'^<SearchQuerySet: query=<test_haystack.mocks.MockSearchQuery object' r' at 0x[0-9A-Fa-f]+>, using=None>$') def test_iter(self): reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) msqs = self.msqs.all() results = [int(res.pk) for res in msqs] self.assertEqual(results, [res.pk for res in MOCK_SEARCH_RESULTS[:23]]) self.assertEqual(len(connections['default'].queries), 3) def test_slice(self): reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) results = self.msqs.all() self.assertEqual([int(res.pk) for res in results[1:11]], [res.pk for res in MOCK_SEARCH_RESULTS[1:11]]) self.assertEqual(len(connections['default'].queries), 1) reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) results = self.msqs.all() self.assertEqual(int(results[22].pk), MOCK_SEARCH_RESULTS[22].pk) self.assertEqual(len(connections['default'].queries), 1) def test_manual_iter(self): results = self.msqs.all() reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) check = [result.pk for result in results._manual_iter()] self.assertEqual(check, [ u'1', u'2', u'3', u'4', u'5', u'6', u'7', u'8', u'9', u'10', u'11', u'12', u'13', u'14', u'15', u'16', u'17', u'18', u'19', u'20', u'21', u'22', u'23' ]) self.assertEqual(len(connections['default'].queries), 3) reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) # Test to ensure we properly fill the cache, even if we get fewer # results back (not a handled model) than the hit count indicates. # This will hang indefinitely if broken. old_ui = self.ui self.ui.build(indexes=[self.cpkmmsi]) connections['default']._index = self.ui self.cpkmmsi.update() results = self.msqs.all() loaded = [result.pk for result in results._manual_iter()] self.assertEqual(loaded, [u'sometext', u'1234']) self.assertEqual(len(connections['default'].queries), 1) connections['default']._index = old_ui def test_fill_cache(self): reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) results = self.msqs.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) reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) # Test to ensure we properly fill the cache, even if we get fewer # results back (not a handled model) than the hit count indicates. sqs = SearchQuerySet().all() sqs.query.backend = MixedMockSearchBackend('default') results = sqs self.assertEqual( len([ result for result in results._result_cache if result is not None ]), 0) self.assertEqual([ int(result.pk) for result in results._result_cache if result is not None ], []) 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 ]), 9) self.assertEqual([ int(result.pk) for result in results._result_cache if result is not None ], [1, 2, 3, 4, 5, 6, 7, 8, 10]) self.assertEqual(len(connections['default'].queries), 2) results._fill_cache(10, 20) self.assertEqual( len([ result for result in results._result_cache if result is not None ]), 17) self.assertEqual([ int(result.pk) for result in results._result_cache if result is not None ], [1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 15, 16, 17, 18, 19, 20]) self.assertEqual(len(connections['default'].queries), 4) results._fill_cache(20, 30) self.assertEqual( len([ result for result in results._result_cache if result is not None ]), 20) self.assertEqual([ int(result.pk) for result in results._result_cache if result is not None ], [ 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 15, 16, 17, 18, 19, 20, 21, 22, 23 ]) self.assertEqual(len(connections['default'].queries), 6) def test_cache_is_full(self): reset_search_queries() self.assertEqual(len(connections['default'].queries), 0) self.assertEqual(self.msqs._cache_is_full(), False) results = self.msqs.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_all(self): sqs = self.msqs.all() self.assertTrue(isinstance(sqs, SearchQuerySet)) def test_filter(self): sqs = self.msqs.filter(content='foo') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.query_filter), 1) def test_exclude(self): sqs = self.msqs.exclude(content='foo') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.query_filter), 1) def test_order_by(self): sqs = self.msqs.order_by('foo') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertTrue('foo' in sqs.query.order_by) def test_models(self): # Stow. old_unified_index = connections['default']._index ui = UnifiedIndex() bmmsi = BasicMockModelSearchIndex() bammsi = BasicAnotherMockModelSearchIndex() ui.build(indexes=[bmmsi, bammsi]) connections['default']._index = ui msqs = SearchQuerySet() sqs = msqs.all() self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.models), 0) sqs = msqs.models(MockModel) self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.models), 1) sqs = msqs.models(MockModel, AnotherMockModel) self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.models), 2) # This will produce a warning. ui.build(indexes=[bmmsi]) sqs = msqs.models(AnotherMockModel) self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.models), 1) def test_result_class(self): sqs = self.msqs.all() self.assertTrue(issubclass(sqs.query.result_class, SearchResult)) # Custom class. class IttyBittyResult(object): pass sqs = self.msqs.result_class(IttyBittyResult) self.assertTrue(issubclass(sqs.query.result_class, IttyBittyResult)) # Reset to default. sqs = self.msqs.result_class(None) self.assertTrue(issubclass(sqs.query.result_class, SearchResult)) def test_boost(self): sqs = self.msqs.boost('foo', 10) self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.boost.keys()), 1) def test_highlight(self): sqs = self.msqs.highlight() self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(sqs.query.highlight, True) def test_spelling(self): # Test the case where spelling support is disabled. sqs = self.msqs.filter(content='Indx') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(sqs.spelling_suggestion(), None) self.assertEqual(sqs.spelling_suggestion('indexy'), None) def test_raw_search(self): self.assertEqual(len(self.msqs.raw_search('foo')), 23) self.assertEqual( len( self.msqs.raw_search( '(content__exact:hello AND content__exact:world)')), 23) def test_load_all(self): # Models with character primary keys. sqs = SearchQuerySet() sqs.query.backend = CharPKMockSearchBackend('charpk') results = sqs.load_all().all() self.assertEqual(len(results._result_cache), 0) results._fill_cache(0, 2) self.assertEqual( len([ result for result in results._result_cache if result is not None ]), 2) # If nothing is handled, you get nothing. old_ui = connections['default']._index ui = UnifiedIndex() ui.build(indexes=[]) connections['default']._index = ui sqs = self.msqs.load_all() self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs), 0) connections['default']._index = old_ui # For full tests, see the solr_backend. def test_load_all_read_queryset(self): # Stow. old_ui = connections['default']._index ui = UnifiedIndex() gafmmsi = GhettoAFifthMockModelSearchIndex() ui.build(indexes=[gafmmsi]) connections['default']._index = ui gafmmsi.update() sqs = SearchQuerySet() results = sqs.load_all().all() results.query.backend = ReadQuerySetMockSearchBackend('default') results._fill_cache(0, 2) # The deleted result isn't returned self.assertEqual( len([ result for result in results._result_cache if result is not None ]), 1) # Register a SearchIndex with a read_queryset that returns deleted items rqstsi = TextReadQuerySetTestSearchIndex() ui.build(indexes=[rqstsi]) rqstsi.update() sqs = SearchQuerySet() results = sqs.load_all().all() results.query.backend = ReadQuerySetMockSearchBackend('default') results._fill_cache(0, 2) # Both the deleted and not deleted items are returned self.assertEqual( len([ result for result in results._result_cache if result is not None ]), 2) # Restore. connections['default']._index = old_ui def test_auto_query(self): sqs = self.msqs.auto_query('test search -stuff') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(repr(sqs.query.query_filter), '<SQ: AND content__contains=test search -stuff>') sqs = self.msqs.auto_query('test "my thing" search -stuff') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual( repr(sqs.query.query_filter), '<SQ: AND content__contains=test "my thing" search -stuff>') sqs = self.msqs.auto_query( 'test "my thing" search \'moar quotes\' -stuff') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual( repr(sqs.query.query_filter), '<SQ: AND content__contains=test "my thing" search \'moar quotes\' -stuff>' ) sqs = self.msqs.auto_query( 'test "my thing" search \'moar quotes\' "foo -stuff') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual( repr(sqs.query.query_filter), '<SQ: AND content__contains=test "my thing" search \'moar quotes\' "foo -stuff>' ) sqs = self.msqs.auto_query('test - stuff') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(repr(sqs.query.query_filter), "<SQ: AND content__contains=test - stuff>") # Ensure bits in exact matches get escaped properly as well. sqs = self.msqs.auto_query('"pants:rule"') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(repr(sqs.query.query_filter), '<SQ: AND content__contains="pants:rule">') # Now with a different fieldname sqs = self.msqs.auto_query('test search -stuff', fieldname='title') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(repr(sqs.query.query_filter), "<SQ: AND title__contains=test search -stuff>") sqs = self.msqs.auto_query('test "my thing" search -stuff', fieldname='title') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual( repr(sqs.query.query_filter), '<SQ: AND title__contains=test "my thing" search -stuff>') def test_count(self): self.assertEqual(self.msqs.count(), 23) def test_facet_counts(self): self.assertEqual(self.msqs.facet_counts(), {}) def test_best_match(self): self.assertTrue(isinstance(self.msqs.best_match(), SearchResult)) def test_latest(self): self.assertTrue(isinstance(self.msqs.latest('pub_date'), SearchResult)) def test_more_like_this(self): mock = MockModel() mock.id = 1 self.assertEqual(len(self.msqs.more_like_this(mock)), 23) def test_facets(self): sqs = self.msqs.facet('foo') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.facets), 1) sqs2 = self.msqs.facet('foo').facet('bar') self.assertTrue(isinstance(sqs2, SearchQuerySet)) self.assertEqual(len(sqs2.query.facets), 2) def test_date_facets(self): try: sqs = self.msqs.date_facet('foo', start_date=datetime.date(2008, 2, 25), end_date=datetime.date(2009, 2, 25), gap_by='smarblaph') self.fail() except FacetingError as e: self.assertEqual( str(e), "The gap_by ('smarblaph') must be one of the following: year, month, day, hour, minute, second." ) sqs = self.msqs.date_facet('foo', start_date=datetime.date(2008, 2, 25), end_date=datetime.date(2009, 2, 25), gap_by='month') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.date_facets), 1) sqs2 = self.msqs.date_facet('foo', start_date=datetime.date(2008, 2, 25), end_date=datetime.date(2009, 2, 25), gap_by='month').date_facet( 'bar', start_date=datetime.date(2007, 2, 25), end_date=datetime.date(2009, 2, 25), gap_by='year') self.assertTrue(isinstance(sqs2, SearchQuerySet)) self.assertEqual(len(sqs2.query.date_facets), 2) def test_query_facets(self): sqs = self.msqs.query_facet('foo', '[bar TO *]') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.query_facets), 1) sqs2 = self.msqs.query_facet('foo', '[bar TO *]').query_facet( 'bar', '[100 TO 499]') self.assertTrue(isinstance(sqs2, SearchQuerySet)) self.assertEqual(len(sqs2.query.query_facets), 2) # Test multiple query facets on a single field sqs3 = self.msqs.query_facet('foo', '[bar TO *]').query_facet( 'bar', '[100 TO 499]').query_facet('foo', '[1000 TO 1499]') self.assertTrue(isinstance(sqs3, SearchQuerySet)) self.assertEqual(len(sqs3.query.query_facets), 3) def test_stats(self): sqs = self.msqs.stats_facet('foo', 'bar') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.stats), 1) sqs2 = self.msqs.stats_facet('foo', 'bar').stats_facet('foo', 'baz') self.assertTrue(isinstance(sqs2, SearchQuerySet)) self.assertEqual(len(sqs2.query.stats), 1) sqs3 = self.msqs.stats_facet('foo', 'bar').stats_facet('moof', 'baz') self.assertTrue(isinstance(sqs3, SearchQuerySet)) self.assertEqual(len(sqs3.query.stats), 2) def test_narrow(self): sqs = self.msqs.narrow('foo:moof') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.narrow_queries), 1) def test_clone(self): results = self.msqs.filter(foo='bar', foo__lt='10') clone = results._clone() self.assertTrue(isinstance(clone, SearchQuerySet)) self.assertEqual(str(clone.query), str(results.query)) self.assertEqual(clone._result_cache, []) self.assertEqual(clone._result_count, None) self.assertEqual(clone._cache_full, False) self.assertEqual(clone._using, results._using) def test_using(self): sqs = SearchQuerySet(using='default') self.assertNotEqual(sqs.query, None) self.assertEqual(sqs.query._using, 'default') def test_chaining(self): sqs = self.msqs.filter(content='foo') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.query_filter), 1) # A second instance should inherit none of the changes from above. sqs = self.msqs.filter(content='bar') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.query_filter), 1) def test_none(self): sqs = self.msqs.none() self.assertTrue(isinstance(sqs, EmptySearchQuerySet)) self.assertEqual(len(sqs), 0) def test___and__(self): sqs1 = self.msqs.filter(content='foo') sqs2 = self.msqs.filter(content='bar') sqs = sqs1 & sqs2 self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.query_filter), 2) def test___or__(self): sqs1 = self.msqs.filter(content='foo') sqs2 = self.msqs.filter(content='bar') sqs = sqs1 | sqs2 self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.query_filter), 2) def test_and_or(self): """ Combining AND queries with OR should give AND(OR(a, b), OR(c, d)) """ sqs1 = self.msqs.filter(content='foo').filter(content='oof') sqs2 = self.msqs.filter(content='bar').filter(content='rab') sqs = sqs1 | sqs2 self.assertEqual(sqs.query.query_filter.connector, 'OR') self.assertEqual(repr(sqs.query.query_filter.children[0]), repr(sqs1.query.query_filter)) self.assertEqual(repr(sqs.query.query_filter.children[1]), repr(sqs2.query.query_filter)) def test_or_and(self): """ Combining OR queries with AND should give OR(AND(a, b), AND(c, d)) """ sqs1 = self.msqs.filter(content='foo').filter_or(content='oof') sqs2 = self.msqs.filter(content='bar').filter_or(content='rab') sqs = sqs1 & sqs2 self.assertEqual(sqs.query.query_filter.connector, 'AND') self.assertEqual(repr(sqs.query.query_filter.children[0]), repr(sqs1.query.query_filter)) self.assertEqual(repr(sqs.query.query_filter.children[1]), repr(sqs2.query.query_filter))
class SearchQuerySetTestCase(TestCase): def setUp(self): super(SearchQuerySetTestCase, self).setUp() self.bsqs = SearchQuerySet(query=DummySearchQuery(backend=DummySearchBackend())) self.msqs = SearchQuerySet(query=MockSearchQuery(backend=MockSearchBackend())) self.mmsqs = SearchQuerySet(query=MockSearchQuery(backend=MixedMockSearchBackend())) # Stow. self.old_debug = settings.DEBUG settings.DEBUG = True self.old_site = haystack.site test_site = SearchSite() test_site.register(MockModel) test_site.register(CharPKMockModel) haystack.site = test_site backends.reset_search_queries() def tearDown(self): # Restore. haystack.site = self.old_site settings.DEBUG = self.old_debug super(SearchQuerySetTestCase, self).tearDown() def test_len(self): # Dummy always returns 0. self.assertEqual(len(self.bsqs), 0) self.assertEqual(len(self.msqs), 100) def test_repr(self): self.assertEqual(repr(self.bsqs), '[]') backends.reset_search_queries() self.assertEqual(len(backends.queries), 0) self.assertEqual(repr(self.msqs), "[<SearchResult: core.MockModel (pk=0)>, <SearchResult: core.MockModel (pk=1)>, <SearchResult: core.MockModel (pk=2)>, <SearchResult: core.MockModel (pk=3)>, <SearchResult: core.MockModel (pk=4)>, <SearchResult: core.MockModel (pk=5)>, <SearchResult: core.MockModel (pk=6)>, <SearchResult: core.MockModel (pk=7)>, <SearchResult: core.MockModel (pk=8)>, <SearchResult: core.MockModel (pk=9)>, <SearchResult: core.MockModel (pk=10)>, <SearchResult: core.MockModel (pk=11)>, <SearchResult: core.MockModel (pk=12)>, <SearchResult: core.MockModel (pk=13)>, <SearchResult: core.MockModel (pk=14)>, <SearchResult: core.MockModel (pk=15)>, <SearchResult: core.MockModel (pk=16)>, <SearchResult: core.MockModel (pk=17)>, <SearchResult: core.MockModel (pk=18)>, '...(remaining elements truncated)...']") self.assertEqual(len(backends.queries), 1) def test_iter(self): # Dummy always returns []. self.assertEqual([result for result in self.bsqs.all()], []) backends.reset_search_queries() self.assertEqual(len(backends.queries), 0) msqs = self.msqs.all() results = [result for result in msqs] self.assertEqual(results, MOCK_SEARCH_RESULTS) self.assertEqual(len(backends.queries), 10) def test_slice(self): backends.reset_search_queries() self.assertEqual(len(backends.queries), 0) results = self.msqs.all() self.assertEqual(results[1:11], MOCK_SEARCH_RESULTS[1:11]) self.assertEqual(len(backends.queries), 1) backends.reset_search_queries() self.assertEqual(len(backends.queries), 0) results = self.msqs.all() self.assertEqual(results[50], MOCK_SEARCH_RESULTS[50]) self.assertEqual(len(backends.queries), 1) def test_manual_iter(self): results = self.msqs.all() backends.reset_search_queries() self.assertEqual(len(backends.queries), 0) for offset, result in enumerate(results._manual_iter()): self.assertEqual(result, MOCK_SEARCH_RESULTS[offset]) self.assertEqual(len(backends.queries), 10) backends.reset_search_queries() self.assertEqual(len(backends.queries), 0) # Test to ensure we properly fill the cache, even if we get fewer # results back (not in the SearchSite) than the hit count indicates. # This will hang indefinitely if broken. results = self.mmsqs.all() loaded = [result.pk for result in results._manual_iter()] self.assertEqual(loaded, [0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 15, 16, 17, 18, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29]) self.assertEqual(len(backends.queries), 8) def test_fill_cache(self): backends.reset_search_queries() self.assertEqual(len(backends.queries), 0) results = self.msqs.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) backends.reset_search_queries() self.assertEqual(len(backends.queries), 0) # Test to ensure we properly fill the cache, even if we get fewer # results back (not in the SearchSite) than the hit count indicates. results = self.mmsqs.all() self.assertEqual(len([result for result in results._result_cache if result is not None]), 0) self.assertEqual([result.pk for result in results._result_cache if result is not None], []) 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]), 9) self.assertEqual([result.pk for result in results._result_cache if result is not None], [0, 1, 2, 3, 4, 5, 6, 7, 8]) self.assertEqual(len(backends.queries), 2) results._fill_cache(10, 20) self.assertEqual(len([result for result in results._result_cache if result is not None]), 17) self.assertEqual([result.pk for result in results._result_cache if result is not None], [0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 15, 16, 17, 18, 19]) self.assertEqual(len(backends.queries), 4) results._fill_cache(20, 30) self.assertEqual(len([result for result in results._result_cache if result is not None]), 27) self.assertEqual([result.pk for result in results._result_cache if result is not None], [0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29]) self.assertEqual(len(backends.queries), 6) def test_cache_is_full(self): # Dummy always has a count of 0 and an empty _result_cache, hence True. self.assertEqual(self.bsqs._cache_is_full(), False) results = self.bsqs.all() fire_the_iterator_and_fill_cache = [result for result in results] self.assertEqual(results._cache_is_full(), True) backends.reset_search_queries() self.assertEqual(len(backends.queries), 0) self.assertEqual(self.msqs._cache_is_full(), False) results = self.msqs.all() fire_the_iterator_and_fill_cache = [result for result in results] self.assertEqual(results._cache_is_full(), True) self.assertEqual(len(backends.queries), 10) def test_all(self): sqs = self.bsqs.all() self.assert_(isinstance(sqs, SearchQuerySet)) def test_filter(self): sqs = self.bsqs.filter(content='foo') self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.query_filter), 1) def test_exclude(self): sqs = self.bsqs.exclude(content='foo') self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.query_filter), 1) def test_order_by(self): sqs = self.bsqs.order_by('foo') self.assert_(isinstance(sqs, SearchQuerySet)) self.assert_('foo' in sqs.query.order_by) def test_models(self): mock_index_site = SearchSite() mock_index_site.register(MockModel) mock_index_site.register(AnotherMockModel) bsqs = SearchQuerySet(site=mock_index_site) sqs = bsqs.all() self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.models), 0) sqs = bsqs.models(MockModel) self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.models), 1) sqs = bsqs.models(MockModel, AnotherMockModel) self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.models), 2) # This will produce a warning. mock_index_site.unregister(AnotherMockModel) sqs = bsqs.models(AnotherMockModel) self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.models), 1) def test_boost(self): sqs = self.bsqs.boost('foo', 10) self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.boost.keys()), 1) def test_highlight(self): sqs = self.bsqs.highlight() self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual(sqs.query.highlight, True) def test_spelling(self): # Test the case where spelling support is disabled. sqs = self.bsqs.filter(content='Indx') self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual(sqs.spelling_suggestion(), None) self.assertEqual(sqs.spelling_suggestion('indexy'), None) def test_raw_search(self): self.assertEqual(len(self.bsqs.raw_search('foo')), 0) self.assertEqual(len(self.bsqs.raw_search('(content__exact hello AND content__exact world)')), 1) def test_load_all(self): # Models with character primary keys sqs = SearchQuerySet(query=MockSearchQuery(backend=CharPKMockSearchBackend())) results = sqs.load_all().all() self.assertEqual(len(results._result_cache), 0) results._fill_cache(0, 2) self.assertEqual(len([result for result in results._result_cache if result is not None]), 2) # If nothing is registered, you get nothing. haystack.site.unregister(MockModel) haystack.site.unregister(CharPKMockModel) sqs = self.msqs.load_all() self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs), 0) # For full tests, see the solr_backend. def test_auto_query(self): sqs = self.bsqs.auto_query('test search -stuff') self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual(repr(sqs.query.query_filter), '<SQ: AND (content__exact=test AND content__exact=search AND NOT (content__exact=stuff))>') sqs = self.bsqs.auto_query('test "my thing" search -stuff') self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual(repr(sqs.query.query_filter), '<SQ: AND (content__exact=my thing AND content__exact=test AND content__exact=search AND NOT (content__exact=stuff))>') sqs = self.bsqs.auto_query('test "my thing" search \'moar quotes\' -stuff') self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual(repr(sqs.query.query_filter), "<SQ: AND (content__exact=my thing AND content__exact=test AND content__exact=search AND content__exact='moar AND content__exact=quotes' AND NOT (content__exact=stuff))>") sqs = self.bsqs.auto_query('test "my thing" search \'moar quotes\' "foo -stuff') self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual(repr(sqs.query.query_filter), '<SQ: AND (content__exact=my thing AND content__exact=test AND content__exact=search AND content__exact=\'moar AND content__exact=quotes\' AND content__exact="foo AND NOT (content__exact=stuff))>') sqs = self.bsqs.auto_query('test - stuff') self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual(repr(sqs.query.query_filter), '<SQ: AND (content__exact=test AND content__exact=- AND content__exact=stuff)>') # Ensure bits in exact matches get escaped properly as well. sqs = self.bsqs.auto_query('"pants:rule"') self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual(repr(sqs.query.query_filter), '<SQ: AND content__exact=pants:rule>') def test_count(self): self.assertEqual(self.bsqs.count(), 0) def test_facet_counts(self): self.assertEqual(self.bsqs.facet_counts(), {}) def test_best_match(self): self.assert_(isinstance(self.msqs.best_match(), SearchResult)) def test_latest(self): self.assert_(isinstance(self.msqs.latest('pub_date'), SearchResult)) def test_more_like_this(self): mock = MockModel() mock.id = 1 self.assertEqual(len(self.msqs.more_like_this(mock)), 100) def test_facets(self): sqs = self.bsqs.facet('foo') self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.facets), 1) sqs2 = self.bsqs.facet('foo').facet('bar') self.assert_(isinstance(sqs2, SearchQuerySet)) self.assertEqual(len(sqs2.query.facets), 2) def test_date_facets(self): try: sqs = self.bsqs.date_facet('foo', start_date=datetime.date(2008, 2, 25), end_date=datetime.date(2009, 2, 25), gap_by='smarblaph') self.fail() except FacetingError, e: self.assertEqual(str(e), "The gap_by ('smarblaph') must be one of the following: year, month, day, hour, minute, second.") sqs = self.bsqs.date_facet('foo', start_date=datetime.date(2008, 2, 25), end_date=datetime.date(2009, 2, 25), gap_by='month') self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.date_facets), 1) sqs2 = self.bsqs.date_facet('foo', start_date=datetime.date(2008, 2, 25), end_date=datetime.date(2009, 2, 25), gap_by='month').date_facet('bar', start_date=datetime.date(2007, 2, 25), end_date=datetime.date(2009, 2, 25), gap_by='year') self.assert_(isinstance(sqs2, SearchQuerySet)) self.assertEqual(len(sqs2.query.date_facets), 2)
class SearchQuerySetTestCase(TestCase): def setUp(self): super(SearchQuerySetTestCase, self).setUp() self.bsqs = SearchQuerySet(query=DummySearchQuery(backend=DummySearchBackend())) self.msqs = SearchQuerySet(query=MockSearchQuery(backend=MockSearchBackend())) self.mmsqs = SearchQuerySet(query=MockSearchQuery(backend=MixedMockSearchBackend())) # Stow. self.old_debug = settings.DEBUG settings.DEBUG = True self.old_site = haystack.site test_site = SearchSite() test_site.register(MockModel) test_site.register(CharPKMockModel) haystack.site = test_site backends.reset_search_queries() def tearDown(self): # Restore. haystack.site = self.old_site settings.DEBUG = self.old_debug super(SearchQuerySetTestCase, self).tearDown() def test_len(self): # Dummy always returns 0. self.assertEqual(len(self.bsqs), 0) self.assertEqual(len(self.msqs), 100) def test_repr(self): self.assertEqual(repr(self.bsqs), '[]') backends.reset_search_queries() self.assertEqual(len(backends.queries), 0) self.assertEqual(repr(self.msqs), "[<SearchResult: core.MockModel (pk=0)>, <SearchResult: core.MockModel (pk=1)>, <SearchResult: core.MockModel (pk=2)>, <SearchResult: core.MockModel (pk=3)>, <SearchResult: core.MockModel (pk=4)>, <SearchResult: core.MockModel (pk=5)>, <SearchResult: core.MockModel (pk=6)>, <SearchResult: core.MockModel (pk=7)>, <SearchResult: core.MockModel (pk=8)>, <SearchResult: core.MockModel (pk=9)>, <SearchResult: core.MockModel (pk=10)>, <SearchResult: core.MockModel (pk=11)>, <SearchResult: core.MockModel (pk=12)>, <SearchResult: core.MockModel (pk=13)>, <SearchResult: core.MockModel (pk=14)>, <SearchResult: core.MockModel (pk=15)>, <SearchResult: core.MockModel (pk=16)>, <SearchResult: core.MockModel (pk=17)>, <SearchResult: core.MockModel (pk=18)>, '...(remaining elements truncated)...']") self.assertEqual(len(backends.queries), 1) def test_iter(self): # Dummy always returns []. self.assertEqual([result for result in self.bsqs.all()], []) backends.reset_search_queries() self.assertEqual(len(backends.queries), 0) msqs = self.msqs.all() results = [result for result in msqs] self.assertEqual(results, MOCK_SEARCH_RESULTS) self.assertEqual(len(backends.queries), 10) def test_slice(self): backends.reset_search_queries() self.assertEqual(len(backends.queries), 0) results = self.msqs.all() self.assertEqual(results[1:11], MOCK_SEARCH_RESULTS[1:11]) self.assertEqual(len(backends.queries), 1) backends.reset_search_queries() self.assertEqual(len(backends.queries), 0) results = self.msqs.all() self.assertEqual(results[50], MOCK_SEARCH_RESULTS[50]) self.assertEqual(len(backends.queries), 1) def test_manual_iter(self): results = self.msqs.all() backends.reset_search_queries() self.assertEqual(len(backends.queries), 0) for offset, result in enumerate(results._manual_iter()): self.assertEqual(result, MOCK_SEARCH_RESULTS[offset]) self.assertEqual(len(backends.queries), 10) backends.reset_search_queries() self.assertEqual(len(backends.queries), 0) # Test to ensure we properly fill the cache, even if we get fewer # results back (not in the SearchSite) than the hit count indicates. # This will hang indefinitely if broken. results = self.mmsqs.all() loaded = [result.pk for result in results._manual_iter()] self.assertEqual(loaded, [0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 15, 16, 17, 18, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29]) self.assertEqual(len(backends.queries), 8) def test_fill_cache(self): backends.reset_search_queries() self.assertEqual(len(backends.queries), 0) results = self.msqs.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) backends.reset_search_queries() self.assertEqual(len(backends.queries), 0) # Test to ensure we properly fill the cache, even if we get fewer # results back (not in the SearchSite) than the hit count indicates. results = self.mmsqs.all() self.assertEqual(len([result for result in results._result_cache if result is not None]), 0) self.assertEqual([result.pk for result in results._result_cache if result is not None], []) 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]), 9) self.assertEqual([result.pk for result in results._result_cache if result is not None], [0, 1, 2, 3, 4, 5, 6, 7, 8]) self.assertEqual(len(backends.queries), 2) results._fill_cache(10, 20) self.assertEqual(len([result for result in results._result_cache if result is not None]), 17) self.assertEqual([result.pk for result in results._result_cache if result is not None], [0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 15, 16, 17, 18, 19]) self.assertEqual(len(backends.queries), 4) results._fill_cache(20, 30) self.assertEqual(len([result for result in results._result_cache if result is not None]), 27) self.assertEqual([result.pk for result in results._result_cache if result is not None], [0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29]) self.assertEqual(len(backends.queries), 6) def test_cache_is_full(self): # Dummy always has a count of 0 and an empty _result_cache, hence True. self.assertEqual(self.bsqs._cache_is_full(), False) results = self.bsqs.all() fire_the_iterator_and_fill_cache = [result for result in results] self.assertEqual(results._cache_is_full(), True) backends.reset_search_queries() self.assertEqual(len(backends.queries), 0) self.assertEqual(self.msqs._cache_is_full(), False) results = self.msqs.all() fire_the_iterator_and_fill_cache = [result for result in results] self.assertEqual(results._cache_is_full(), True) self.assertEqual(len(backends.queries), 10) def test_all(self): sqs = self.bsqs.all() self.assert_(isinstance(sqs, SearchQuerySet)) def test_filter(self): sqs = self.bsqs.filter(content='foo') self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.query_filter), 1) def test_exclude(self): sqs = self.bsqs.exclude(content='foo') self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.query_filter), 1) def test_order_by(self): sqs = self.bsqs.order_by('foo') self.assert_(isinstance(sqs, SearchQuerySet)) self.assert_('foo' in sqs.query.order_by) def test_models(self): mock_index_site = SearchSite() mock_index_site.register(MockModel) mock_index_site.register(AnotherMockModel) bsqs = SearchQuerySet(site=mock_index_site) sqs = bsqs.all() self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.models), 0) sqs = bsqs.models(MockModel) self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.models), 1) sqs = bsqs.models(MockModel, AnotherMockModel) self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.models), 2) # This will produce a warning. mock_index_site.unregister(AnotherMockModel) sqs = bsqs.models(AnotherMockModel) self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.models), 1) def test_result_class(self): sqs = self.bsqs.all() self.assertTrue(issubclass(sqs.query.result_class, SearchResult)) # Custom class. class IttyBittyResult(object): pass sqs = self.bsqs.result_class(IttyBittyResult) self.assertTrue(issubclass(sqs.query.result_class, IttyBittyResult)) # Reset to default. sqs = self.bsqs.result_class(None) self.assertTrue(issubclass(sqs.query.result_class, SearchResult)) def test_boost(self): sqs = self.bsqs.boost('foo', 10) self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.boost.keys()), 1) def test_highlight(self): sqs = self.bsqs.highlight() self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual(sqs.query.highlight, True) def test_spelling(self): # Test the case where spelling support is disabled. sqs = self.bsqs.filter(content='Indx') self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual(sqs.spelling_suggestion(), None) self.assertEqual(sqs.spelling_suggestion('indexy'), None) def test_raw_search(self): self.assertEqual(len(self.bsqs.raw_search('foo')), 0) self.assertEqual(len(self.bsqs.raw_search('(content__exact hello AND content__exact world)')), 1) def test_load_all(self): # Models with character primary keys sqs = SearchQuerySet(query=MockSearchQuery(backend=CharPKMockSearchBackend())) results = sqs.load_all().all() self.assertEqual(len(results._result_cache), 0) results._fill_cache(0, 2) self.assertEqual(len([result for result in results._result_cache if result is not None]), 2) # If nothing is registered, you get nothing. haystack.site.unregister(MockModel) haystack.site.unregister(CharPKMockModel) sqs = self.msqs.load_all() self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs), 0) # For full tests, see the solr_backend. def test_auto_query(self): sqs = self.bsqs.auto_query('test search -stuff') self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual(repr(sqs.query.query_filter), '<SQ: AND (content__exact=test AND content__exact=search AND NOT (content__exact=stuff))>') sqs = self.bsqs.auto_query('test "my thing" search -stuff') self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual(repr(sqs.query.query_filter), '<SQ: AND (content__exact=my thing AND content__exact=test AND content__exact=search AND NOT (content__exact=stuff))>') sqs = self.bsqs.auto_query('test "my thing" search \'moar quotes\' -stuff') self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual(repr(sqs.query.query_filter), "<SQ: AND (content__exact=my thing AND content__exact=test AND content__exact=search AND content__exact='moar AND content__exact=quotes' AND NOT (content__exact=stuff))>") sqs = self.bsqs.auto_query('test "my thing" search \'moar quotes\' "foo -stuff') self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual(repr(sqs.query.query_filter), '<SQ: AND (content__exact=my thing AND content__exact=test AND content__exact=search AND content__exact=\'moar AND content__exact=quotes\' AND content__exact="foo AND NOT (content__exact=stuff))>') sqs = self.bsqs.auto_query('test - stuff') self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual(repr(sqs.query.query_filter), '<SQ: AND (content__exact=test AND content__exact=- AND content__exact=stuff)>') # Ensure bits in exact matches get escaped properly as well. sqs = self.bsqs.auto_query('"pants:rule"') self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual(repr(sqs.query.query_filter), '<SQ: AND content__exact=pants:rule>') def test_count(self): self.assertEqual(self.bsqs.count(), 0) def test_facet_counts(self): self.assertEqual(self.bsqs.facet_counts(), {}) def test_best_match(self): self.assert_(isinstance(self.msqs.best_match(), SearchResult)) def test_latest(self): self.assert_(isinstance(self.msqs.latest('pub_date'), SearchResult)) def test_more_like_this(self): mock = MockModel() mock.id = 1 self.assertEqual(len(self.msqs.more_like_this(mock)), 100) def test_facets(self): sqs = self.bsqs.facet('foo') self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.facets), 1) sqs2 = self.bsqs.facet('foo').facet('bar') self.assert_(isinstance(sqs2, SearchQuerySet)) self.assertEqual(len(sqs2.query.facets), 2) def test_date_facets(self): try: sqs = self.bsqs.date_facet('foo', start_date=datetime.date(2008, 2, 25), end_date=datetime.date(2009, 2, 25), gap_by='smarblaph') self.fail() except FacetingError, e: self.assertEqual(str(e), "The gap_by ('smarblaph') must be one of the following: year, month, day, hour, minute, second.") sqs = self.bsqs.date_facet('foo', start_date=datetime.date(2008, 2, 25), end_date=datetime.date(2009, 2, 25), gap_by='month') self.assert_(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.date_facets), 1) sqs2 = self.bsqs.date_facet('foo', start_date=datetime.date(2008, 2, 25), end_date=datetime.date(2009, 2, 25), gap_by='month').date_facet('bar', start_date=datetime.date(2007, 2, 25), end_date=datetime.date(2009, 2, 25), gap_by='year') self.assert_(isinstance(sqs2, SearchQuerySet)) self.assertEqual(len(sqs2.query.date_facets), 2)
class LiveSolrMoreLikeThisTestCase(TestCase): fixtures = ["solr_bulk_data.json"] def setUp(self): super(LiveSolrMoreLikeThisTestCase, self).setUp() self.sqs = SearchQuerySet() # Wipe it clean. self.sqs.query.backend.clear() # With the models registered, you get the proper bits. import haystack from haystack.sites import SearchSite # Stow. self.old_site = haystack.site test_site = SearchSite() test_site.register(MockModel, SolrMockModelSearchIndex) test_site.register(AnotherMockModel, SolrAnotherMockModelSearchIndex) haystack.site = test_site # Force indexing of the content. for mock in MockModel.objects.all(): mock.save() # Force indexing of the content. for mock in AnotherMockModel.objects.all(): mock.save() self.sqs = SearchQuerySet() def tearDown(self): # Wipe it clean. self.sqs.query.backend.clear() # Restore. import haystack haystack.site = self.old_site super(LiveSolrMoreLikeThisTestCase, self).tearDown() def test_more_like_this(self): mlt = self.sqs.more_like_this(MockModel.objects.get(pk=1)) self.assertEqual(mlt.count(), 25) self.assertEqual( [result.pk for result in mlt], [ "6", "14", "4", "10", "22", "5", "3", "12", "2", "23", "18", "19", "13", "7", "15", "21", "9", "1", "2", "20", "16", "17", "8", "11", ], ) alt_mlt = self.sqs.filter(name="daniel3").more_like_this(MockModel.objects.get(pk=3)) self.assertEqual(alt_mlt.count(), 11) self.assertEqual([result.pk for result in alt_mlt], ["23", "13", "17", "16", "22", "19", "4", "10", "1", "2"]) alt_mlt_with_models = self.sqs.models(MockModel).more_like_this(MockModel.objects.get(pk=1)) self.assertEqual(alt_mlt_with_models.count(), 23) self.assertEqual( [result.pk for result in alt_mlt_with_models], [ "6", "14", "4", "10", "22", "5", "3", "12", "2", "23", "18", "19", "13", "7", "15", "21", "9", "20", "16", "17", "8", "11", ], )
class SearchQuerySetTestCase(TestCase): fixtures = ["base_data.json", "bulk_data.json"] def setUp(self): super(SearchQuerySetTestCase, self).setUp() # Stow. self.old_unified_index = connections["default"]._index self.ui = UnifiedIndex() self.bmmsi = BasicMockModelSearchIndex() self.cpkmmsi = CharPKMockModelSearchIndex() self.ui.build(indexes=[self.bmmsi, self.cpkmmsi]) connections["default"]._index = self.ui # Update the "index". backend = connections["default"].get_backend() backend.clear() backend.update(self.bmmsi, MockModel.objects.all()) self.msqs = SearchQuerySet() # Stow. reset_search_queries() def tearDown(self): # Restore. connections["default"]._index = self.old_unified_index super(SearchQuerySetTestCase, self).tearDown() def test_len(self): self.assertEqual(len(self.msqs), 23) def test_repr(self): reset_search_queries() self.assertEqual(len(connections["default"].queries), 0) self.assertRegexpMatches( repr(self.msqs), r"^<SearchQuerySet: query=<test_haystack.mocks.MockSearchQuery object" r" at 0x[0-9A-Fa-f]+>, using=None>$", ) def test_iter(self): reset_search_queries() self.assertEqual(len(connections["default"].queries), 0) msqs = self.msqs.all() results = [int(res.pk) for res in iter(msqs)] self.assertEqual(results, [res.pk for res in MOCK_SEARCH_RESULTS[:23]]) self.assertEqual(len(connections["default"].queries), 3) def test_slice(self): reset_search_queries() self.assertEqual(len(connections["default"].queries), 0) results = self.msqs.all() self.assertEqual([int(res.pk) for res in results[1:11]], [res.pk for res in MOCK_SEARCH_RESULTS[1:11]]) self.assertEqual(len(connections["default"].queries), 1) reset_search_queries() self.assertEqual(len(connections["default"].queries), 0) results = self.msqs.all() self.assertEqual(int(results[22].pk), MOCK_SEARCH_RESULTS[22].pk) self.assertEqual(len(connections["default"].queries), 1) def test_manual_iter(self): results = self.msqs.all() reset_search_queries() self.assertEqual(len(connections["default"].queries), 0) check = [result.pk for result in results._manual_iter()] self.assertEqual( check, [ "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", ], ) self.assertEqual(len(connections["default"].queries), 3) reset_search_queries() self.assertEqual(len(connections["default"].queries), 0) # Test to ensure we properly fill the cache, even if we get fewer # results back (not a handled model) than the hit count indicates. # This will hang indefinitely if broken. old_ui = self.ui self.ui.build(indexes=[self.cpkmmsi]) connections["default"]._index = self.ui self.cpkmmsi.update() results = self.msqs.all() loaded = [result.pk for result in results._manual_iter()] self.assertEqual(loaded, ["sometext", "1234"]) self.assertEqual(len(connections["default"].queries), 1) connections["default"]._index = old_ui def test_cache_is_full(self): reset_search_queries() self.assertEqual(len(connections["default"].queries), 0) self.assertEqual(self.msqs._cache_is_full(), False) results = self.msqs.all() fire_the_iterator_and_fill_cache = list(results) self.assertEqual(23, len(fire_the_iterator_and_fill_cache)) self.assertEqual(results._cache_is_full(), True) self.assertEqual(len(connections["default"].queries), 4) def test_all(self): sqs = self.msqs.all() self.assertTrue(isinstance(sqs, SearchQuerySet)) def test_filter(self): sqs = self.msqs.filter(content="foo") self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.query_filter), 1) def test_exclude(self): sqs = self.msqs.exclude(content="foo") self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.query_filter), 1) def test_order_by(self): sqs = self.msqs.order_by("foo") self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertTrue("foo" in sqs.query.order_by) def test_models(self): # Stow. old_unified_index = connections["default"]._index ui = UnifiedIndex() bmmsi = BasicMockModelSearchIndex() bammsi = BasicAnotherMockModelSearchIndex() ui.build(indexes=[bmmsi, bammsi]) connections["default"]._index = ui msqs = SearchQuerySet() sqs = msqs.all() self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.models), 0) sqs = msqs.models(MockModel) self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.models), 1) sqs = msqs.models(MockModel, AnotherMockModel) self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.models), 2) # This will produce a warning. ui.build(indexes=[bmmsi]) sqs = msqs.models(AnotherMockModel) self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.models), 1) def test_result_class(self): sqs = self.msqs.all() self.assertTrue(issubclass(sqs.query.result_class, SearchResult)) # Custom class. class IttyBittyResult(object): pass sqs = self.msqs.result_class(IttyBittyResult) self.assertTrue(issubclass(sqs.query.result_class, IttyBittyResult)) # Reset to default. sqs = self.msqs.result_class(None) self.assertTrue(issubclass(sqs.query.result_class, SearchResult)) def test_boost(self): sqs = self.msqs.boost("foo", 10) self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.boost.keys()), 1) def test_highlight(self): sqs = self.msqs.highlight() self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(sqs.query.highlight, True) def test_spelling_override(self): sqs = self.msqs.filter(content="not the spellchecking query") self.assertEqual(sqs.query.spelling_query, None) sqs = self.msqs.set_spelling_query("override") self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(sqs.query.spelling_query, "override") def test_spelling_suggestions(self): # Test the case where spelling support is disabled. sqs = self.msqs.filter(content="Indx") self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(sqs.spelling_suggestion(), None) self.assertEqual(sqs.spelling_suggestion("indexy"), None) def test_raw_search(self): self.assertEqual(len(self.msqs.raw_search("foo")), 23) self.assertEqual(len(self.msqs.raw_search("(content__exact:hello AND content__exact:world)")), 23) def test_load_all(self): # Models with character primary keys. sqs = SearchQuerySet() sqs.query.backend = CharPKMockSearchBackend("charpk") results = sqs.load_all().all() self.assertEqual(len(results._result_cache), 0) results._fill_cache(0, 2) self.assertEqual(len([result for result in results._result_cache if result is not None]), 2) # If nothing is handled, you get nothing. old_ui = connections["default"]._index ui = UnifiedIndex() ui.build(indexes=[]) connections["default"]._index = ui sqs = self.msqs.load_all() self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs), 0) connections["default"]._index = old_ui # For full tests, see the solr_backend. def test_load_all_read_queryset(self): # Stow. old_ui = connections["default"]._index ui = UnifiedIndex() gafmmsi = GhettoAFifthMockModelSearchIndex() ui.build(indexes=[gafmmsi]) connections["default"]._index = ui gafmmsi.update() sqs = SearchQuerySet() results = sqs.load_all().all() results.query.backend = ReadQuerySetMockSearchBackend("default") results._fill_cache(0, 2) # The deleted result isn't returned self.assertEqual(len([result for result in results._result_cache if result is not None]), 1) # Register a SearchIndex with a read_queryset that returns deleted items rqstsi = TextReadQuerySetTestSearchIndex() ui.build(indexes=[rqstsi]) rqstsi.update() sqs = SearchQuerySet() results = sqs.load_all().all() results.query.backend = ReadQuerySetMockSearchBackend("default") results._fill_cache(0, 2) # Both the deleted and not deleted items are returned self.assertEqual(len([result for result in results._result_cache if result is not None]), 2) # Restore. connections["default"]._index = old_ui def test_auto_query(self): sqs = self.msqs.auto_query("test search -stuff") self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(repr(sqs.query.query_filter), "<SQ: AND content__content=test search -stuff>") sqs = self.msqs.auto_query('test "my thing" search -stuff') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(repr(sqs.query.query_filter), '<SQ: AND content__content=test "my thing" search -stuff>') sqs = self.msqs.auto_query("test \"my thing\" search 'moar quotes' -stuff") self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual( repr(sqs.query.query_filter), "<SQ: AND content__content=test \"my thing\" search 'moar quotes' -stuff>" ) sqs = self.msqs.auto_query('test "my thing" search \'moar quotes\' "foo -stuff') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual( repr(sqs.query.query_filter), '<SQ: AND content__content=test "my thing" search \'moar quotes\' "foo -stuff>', ) sqs = self.msqs.auto_query("test - stuff") self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(repr(sqs.query.query_filter), "<SQ: AND content__content=test - stuff>") # Ensure bits in exact matches get escaped properly as well. sqs = self.msqs.auto_query('"pants:rule"') self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(repr(sqs.query.query_filter), '<SQ: AND content__content="pants:rule">') # Now with a different fieldname sqs = self.msqs.auto_query("test search -stuff", fieldname="title") self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(repr(sqs.query.query_filter), "<SQ: AND title__content=test search -stuff>") sqs = self.msqs.auto_query('test "my thing" search -stuff', fieldname="title") self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(repr(sqs.query.query_filter), '<SQ: AND title__content=test "my thing" search -stuff>') def test_count(self): self.assertEqual(self.msqs.count(), 23) def test_facet_counts(self): self.assertEqual(self.msqs.facet_counts(), {}) def test_best_match(self): self.assertTrue(isinstance(self.msqs.best_match(), SearchResult)) def test_latest(self): self.assertTrue(isinstance(self.msqs.latest("pub_date"), SearchResult)) def test_more_like_this(self): mock = MockModel() mock.id = 1 self.assertEqual(len(self.msqs.more_like_this(mock)), 23) def test_facets(self): sqs = self.msqs.facet("foo") self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.facets), 1) sqs2 = self.msqs.facet("foo").facet("bar") self.assertTrue(isinstance(sqs2, SearchQuerySet)) self.assertEqual(len(sqs2.query.facets), 2) def test_date_facets(self): try: sqs = self.msqs.date_facet( "foo", start_date=datetime.date(2008, 2, 25), end_date=datetime.date(2009, 2, 25), gap_by="smarblaph" ) self.fail() except FacetingError as e: self.assertEqual( str(e), "The gap_by ('smarblaph') must be one of the following: year, month, day, hour, minute, second." ) sqs = self.msqs.date_facet( "foo", start_date=datetime.date(2008, 2, 25), end_date=datetime.date(2009, 2, 25), gap_by="month" ) self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.date_facets), 1) sqs2 = self.msqs.date_facet( "foo", start_date=datetime.date(2008, 2, 25), end_date=datetime.date(2009, 2, 25), gap_by="month" ).date_facet("bar", start_date=datetime.date(2007, 2, 25), end_date=datetime.date(2009, 2, 25), gap_by="year") self.assertTrue(isinstance(sqs2, SearchQuerySet)) self.assertEqual(len(sqs2.query.date_facets), 2) def test_query_facets(self): sqs = self.msqs.query_facet("foo", "[bar TO *]") self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.query_facets), 1) sqs2 = self.msqs.query_facet("foo", "[bar TO *]").query_facet("bar", "[100 TO 499]") self.assertTrue(isinstance(sqs2, SearchQuerySet)) self.assertEqual(len(sqs2.query.query_facets), 2) # Test multiple query facets on a single field sqs3 = ( self.msqs.query_facet("foo", "[bar TO *]") .query_facet("bar", "[100 TO 499]") .query_facet("foo", "[1000 TO 1499]") ) self.assertTrue(isinstance(sqs3, SearchQuerySet)) self.assertEqual(len(sqs3.query.query_facets), 3) def test_stats(self): sqs = self.msqs.stats_facet("foo", "bar") self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.stats), 1) sqs2 = self.msqs.stats_facet("foo", "bar").stats_facet("foo", "baz") self.assertTrue(isinstance(sqs2, SearchQuerySet)) self.assertEqual(len(sqs2.query.stats), 1) sqs3 = self.msqs.stats_facet("foo", "bar").stats_facet("moof", "baz") self.assertTrue(isinstance(sqs3, SearchQuerySet)) self.assertEqual(len(sqs3.query.stats), 2) def test_narrow(self): sqs = self.msqs.narrow("foo:moof") self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.narrow_queries), 1) def test_clone(self): results = self.msqs.filter(foo="bar", foo__lt="10") clone = results._clone() self.assertTrue(isinstance(clone, SearchQuerySet)) self.assertEqual(str(clone.query), str(results.query)) self.assertEqual(clone._result_cache, []) self.assertEqual(clone._result_count, None) self.assertEqual(clone._cache_full, False) self.assertEqual(clone._using, results._using) def test_using(self): sqs = SearchQuerySet(using="default") self.assertNotEqual(sqs.query, None) self.assertEqual(sqs.query._using, "default") def test_chaining(self): sqs = self.msqs.filter(content="foo") self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.query_filter), 1) # A second instance should inherit none of the changes from above. sqs = self.msqs.filter(content="bar") self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.query_filter), 1) def test_none(self): sqs = self.msqs.none() self.assertTrue(isinstance(sqs, EmptySearchQuerySet)) self.assertEqual(len(sqs), 0) def test___and__(self): sqs1 = self.msqs.filter(content="foo") sqs2 = self.msqs.filter(content="bar") sqs = sqs1 & sqs2 self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.query_filter), 2) def test___or__(self): sqs1 = self.msqs.filter(content="foo") sqs2 = self.msqs.filter(content="bar") sqs = sqs1 | sqs2 self.assertTrue(isinstance(sqs, SearchQuerySet)) self.assertEqual(len(sqs.query.query_filter), 2) def test_and_or(self): """ Combining AND queries with OR should give AND(OR(a, b), OR(c, d)) """ sqs1 = self.msqs.filter(content="foo").filter(content="oof") sqs2 = self.msqs.filter(content="bar").filter(content="rab") sqs = sqs1 | sqs2 self.assertEqual(sqs.query.query_filter.connector, "OR") self.assertEqual(repr(sqs.query.query_filter.children[0]), repr(sqs1.query.query_filter)) self.assertEqual(repr(sqs.query.query_filter.children[1]), repr(sqs2.query.query_filter)) def test_or_and(self): """ Combining OR queries with AND should give OR(AND(a, b), AND(c, d)) """ sqs1 = self.msqs.filter(content="foo").filter_or(content="oof") sqs2 = self.msqs.filter(content="bar").filter_or(content="rab") sqs = sqs1 & sqs2 self.assertEqual(sqs.query.query_filter.connector, "AND") self.assertEqual(repr(sqs.query.query_filter.children[0]), repr(sqs1.query.query_filter)) self.assertEqual(repr(sqs.query.query_filter.children[1]), repr(sqs2.query.query_filter))
class LiveWhooshMoreLikeThisTestCase(TestCase): fixtures = ['bulk_data.json'] def setUp(self): super(LiveWhooshMoreLikeThisTestCase, self).setUp() # Stow. temp_path = os.path.join('tmp', 'test_whoosh_query') self.old_whoosh_path = settings.HAYSTACK_CONNECTIONS['default']['PATH'] settings.HAYSTACK_CONNECTIONS['default']['PATH'] = temp_path self.old_ui = connections['default'].get_unified_index() self.ui = UnifiedIndex() self.wmmi = WhooshMockSearchIndex() self.wamsi = WhooshAnotherMockSearchIndex() self.ui.build(indexes=[self.wmmi, self.wamsi]) self.sb = connections['default'].get_backend() connections['default']._index = self.ui self.sb.setup() self.raw_whoosh = self.sb.index self.parser = QueryParser(self.sb.content_field_name, schema=self.sb.schema) self.sb.delete_index() self.wmmi.update() self.wamsi.update() self.sqs = SearchQuerySet() def tearDown(self): if os.path.exists(settings.HAYSTACK_CONNECTIONS['default']['PATH']): shutil.rmtree(settings.HAYSTACK_CONNECTIONS['default']['PATH']) settings.HAYSTACK_CONNECTIONS['default']['PATH'] = self.old_whoosh_path connections['default']._index = self.old_ui super(LiveWhooshMoreLikeThisTestCase, self).tearDown() def test_more_like_this(self): mlt = self.sqs.more_like_this(MockModel.objects.get(pk=22)) self.assertEqual(mlt.count(), 22) self.assertEqual( sorted([result.pk for result in mlt]), sorted([ u'9', u'8', u'7', u'6', u'5', u'4', u'3', u'2', u'1', u'21', u'20', u'19', u'18', u'17', u'16', u'15', u'14', u'13', u'12', u'11', u'10', u'23' ])) self.assertEqual(len([result.pk for result in mlt]), 22) alt_mlt = self.sqs.filter(name='daniel3').more_like_this( MockModel.objects.get(pk=13)) self.assertEqual(alt_mlt.count(), 8) self.assertEqual( sorted([result.pk for result in alt_mlt]), sorted([u'4', u'3', u'22', u'19', u'17', u'16', u'10', u'23'])) self.assertEqual(len([result.pk for result in alt_mlt]), 8) alt_mlt_with_models = self.sqs.models(MockModel).more_like_this( MockModel.objects.get(pk=11)) self.assertEqual(alt_mlt_with_models.count(), 22) self.assertEqual( sorted([result.pk for result in alt_mlt_with_models]), sorted([ u'9', u'8', u'7', u'6', u'5', u'4', u'3', u'2', u'1', u'22', u'21', u'20', u'19', u'18', u'17', u'16', u'15', u'14', u'13', u'12', u'10', u'23' ])) self.assertEqual(len([result.pk for result in alt_mlt_with_models]), 22) if hasattr(MockModel.objects, 'defer'): # Make sure MLT works with deferred bits. mi = MockModel.objects.defer('foo').get(pk=21) self.assertEqual(mi._deferred, True) deferred = self.sqs.models(MockModel).more_like_this(mi) self.assertEqual(deferred.count(), 0) self.assertEqual([result.pk for result in deferred], []) self.assertEqual(len([result.pk for result in deferred]), 0) # Ensure that swapping the ``result_class`` works. self.assertTrue( isinstance( self.sqs.result_class(MockSearchResult).more_like_this( MockModel.objects.get(pk=21))[0], MockSearchResult))