def test_searchqueryset_using(self): # Using the default. sqs = SearchQuerySet() self.assertEqual(sqs.count(), 5) self.assertEqual(sqs.models(Foo).count(), 2) self.assertEqual(sqs.models(Bar).count(), 3) self.assertEqual(sqs.using("default").count(), 5) self.assertEqual(sqs.using("default").models(Foo).count(), 2) self.assertEqual(sqs.using("default").models(Bar).count(), 3) self.assertEqual(sqs.using("whoosh").count(), 2) self.assertEqual(sqs.using("whoosh").models(Foo).count(), 2) self.assertEqual(sqs.using("whoosh").models(Bar).count(), 0)
def test_searchqueryset_using(self): # Using the default. sqs = SearchQuerySet('solr') self.assertEqual(sqs.count(), 5) self.assertEqual(sqs.models(Foo).count(), 2) self.assertEqual(sqs.models(Bar).count(), 3) self.assertEqual(sqs.using('solr').count(), 5) self.assertEqual(sqs.using('solr').models(Foo).count(), 2) self.assertEqual(sqs.using('solr').models(Bar).count(), 3) self.assertEqual(sqs.using('whoosh').count(), 2) self.assertEqual(sqs.using('whoosh').models(Foo).count(), 2) self.assertEqual(sqs.using('whoosh').models(Bar).count(), 0)
def queryset(self, postdata, exclude=None): """ Build the `self.model` queryset limited to selected filters. """ if not self.qs: #qs = self.model.objects.all().select_related() from haystack.query import SearchQuerySet qs = SearchQuerySet() if self.connection: qs = qs.using(self.connection) qs = qs.filter(indexed_model_name__in=[self.model.__name__], **self.global_filters) else: qs = self.qs filters = { } # Then for each filter for option in self.options: # If filter is not excluded explicitly (used to get counts # for choices). if option == exclude: continue # If filter contains valid data, check jQuery style encoding of array params if option.field_name not in postdata and option.field_name+"[]" not in postdata: continue # Do filtering if option.filter is not None: qs_ = option.filter(qs, postdata) if isinstance(qs_, dict): filters.update(qs_) else: qs = qs_ else: def clean_values(x): for y in x: if y in ("true", "on"): yield True elif y == "false": yield False else: yield y values = list(clean_values(postdata.getlist(option.field_name)+postdata.getlist(option.field_name+"[]"))) if option.type == "text": # For full-text searching, don't use __in so that the search # backend does its usual query operation. filters[option.field_name] = " ".join(values) elif not u'__ALL__' in values: # if __ALL__ value presents in filter values # then do not limit queryset filters['%s__in' % option.field_name] = values # apply filters simultaneously so that filters on related objects are applied # to the same related object. if they were applied chained (i.e. filter().filter()) # then they could apply to different objects. if len(filters) > 0: qs = qs.filter(**filters) for name, key, default in self.sort_options: if postdata.get("sort", "") == key: qs = qs.order_by(key) # Django ORM but not Haystack if hasattr(qs, 'distinct'): return qs.distinct() # Haystack but not Django ORM else: # Revise the SearchQuerySet to iterate over model objects # rather than SearchResult objects. class SR: def __init__(self, qs): self.qs = qs def facet(self, field, **kwargs): return self.qs.facet(field, **kwargs) def count(self): return len(self.qs) def order_by(self, field): return SR(self.qs.order_by(field)) def __len__(self): return len(self.qs) def __getitem__(self, index): # slices too return SR(self.qs[index]) def __iter__(self): for item in self.qs: yield item.object return SR(qs)
def queryset(self, postdata, exclude=None): """ Build the `self.model` queryset limited to selected filters. """ from haystack.query import SearchQuerySet if self.qs is None: #qs = self.model.objects.all().select_related() qs = SearchQuerySet() if self.connection: qs = qs.using(self.connection) qs = qs.filter(indexed_model_name__in=[self.model.__name__], **self.global_filters) else: qs = self.qs filters = {} filters2 = [] # Then for each filter for option in self.options: # If filter is not excluded explicitly (used to get counts # for choices). if option == exclude: continue # If filter contains valid data, check jQuery style encoding of array params if option.field_name not in postdata and option.field_name + "[]" not in postdata: continue # Do filtering if option.filter is not None: qs_ = option.filter(qs, postdata) if isinstance(qs_, dict): filters.update(qs_) else: qs = qs_ else: values = postdata.getlist( option.field_name) + postdata.getlist(option.field_name + "[]") if option.type == "text": # For full-text searching, don't use __in so that the search # backend does its usual query operation. values = " ".join( values ) # should not really be more than one, but in case the parameter is specified multiple times in the query string if self.qs is None: # This is a Haystack search. Handle text a little differently. # Wrap it in an AutoQuery so advanced search options like quoted phrases are used. # Query both text and text_boosted, which has a boost applied at the field level. from haystack.query import SQ from haystack.inputs import AutoQuery values = AutoQuery(values) filters2.append( SQ(text=values) | SQ(text_boosted=values)) else: filters[option.orm_field_name] = values elif not '__ALL__' in values: # if __ALL__ value presents in filter values # then do not limit queryset def parse_booleans(x): for y in x: if y in ("true", "on"): yield True elif y == "false": yield False else: yield y values = list(parse_booleans(values)) filters['%s__in' % option.orm_field_name] = values # apply filters simultaneously so that filters on related objects are applied # to the same related object. if they were applied chained (i.e. filter().filter()) # then they could apply to different objects. if len(filters) + len(filters2) > 0: qs = qs.filter(*filters2, **filters) for name, key, default, func in self.sort_options: if postdata.get("sort", "") == key: if not func: qs = qs.order_by(key) else: qs = func(qs) # Django ORM but not Haystack if hasattr(qs, 'distinct'): return qs.distinct() # Haystack but not Django ORM elif isinstance(qs, SearchQuerySet): # Revise the SearchQuerySet to iterate over model objects # rather than SearchResult objects. class SR: def __init__(self, qs, manager): self.qs = qs self.manager = manager def facet(self, field, **kwargs): return self.qs.facet(field, **kwargs) def count(self): return len(self.qs) def order_by(self, field): return SR(self.qs.order_by(field), self.manager) def __len__(self): return len(self.qs) def __getitem__(self, index): # slices too, yields a list? return SR(self.qs[index], self.manager) def __iter__(self): # Pre-load all objects in bulk. Then yield in the right order. objects = self.manager.bulk_loader(item.pk for item in self.qs) for item in self.qs: yield objects[int(item.pk)] return SR(qs, self) else: raise ValueError(qs)
def test_handle_delete(self): # Because the code here is pretty leaky (abstraction-wise), we'll test # the actual setup. # First, ensure the signal is setup. self.assertEqual(len(models.signals.post_delete.receivers), 1) # Second, check the existing search data. sqs = SearchQuerySet() self.assertEqual(sqs.using("default").count(), 5) self.assertEqual(sqs.using("default").models(Foo).count(), 2) self.assertEqual(sqs.using("default").models(Bar).count(), 3) self.assertEqual(sqs.using("whoosh").count(), 2) self.assertEqual(sqs.using("whoosh").models(Foo).count(), 2) self.assertEqual(sqs.using("default").models(Foo).order_by("django_id")[0].text, "foo 1") self.assertEqual(sqs.using("whoosh").models(Foo).order_by("django_id")[0].text, "foo 1") # Third, delete the model, which should fire the signal & remove the # record from the index. self.foo_1.delete() # Fourth, check the search data for the now-removed data, making sure counts # have changed correctly. sqs = SearchQuerySet() self.assertEqual(sqs.using("default").count(), 4) self.assertEqual(sqs.using("default").models(Foo).count(), 1) self.assertEqual(sqs.using("default").models(Bar).count(), 3) self.assertEqual(sqs.using("whoosh").count(), 2) self.assertEqual(sqs.using("whoosh").models(Foo).count(), 2) self.assertEqual(sqs.using("default").models(Foo).order_by("django_id")[0].text, "foo 2") self.assertEqual(sqs.using("whoosh").models(Foo).order_by("django_id")[0].text, "foo 1")
def test_handle_save(self): # Because the code here is pretty leaky (abstraction-wise), we'll test # the actual setup. # First, ensure the signal is setup. self.assertEqual(len(models.signals.post_save.receivers), 1) # Second, check the existing search data. sqs = SearchQuerySet() self.assertEqual(sqs.using("default").count(), 5) self.assertEqual(sqs.using("default").models(Foo).count(), 2) self.assertEqual(sqs.using("default").models(Bar).count(), 3) self.assertEqual(sqs.using("whoosh").count(), 2) self.assertEqual(sqs.using("whoosh").models(Foo).count(), 2) self.assertEqual(sqs.using("default").models(Foo).order_by("django_id")[0].text, "foo 1") self.assertEqual(sqs.using("whoosh").models(Foo).order_by("django_id")[0].text, "foo 1") # Third, save the model, which should fire the signal & index the # new data. self.foo_1.body = "A different body" self.foo_1.save() # Fourth, check the search data for the updated data, making sure counts # haven't changed. sqs = SearchQuerySet() self.assertEqual(sqs.using("default").count(), 5) self.assertEqual(sqs.using("default").models(Foo).count(), 2) self.assertEqual(sqs.using("default").models(Bar).count(), 3) self.assertEqual(sqs.using("whoosh").count(), 2) self.assertEqual(sqs.using("whoosh").models(Foo).count(), 2) self.assertEqual(sqs.using("default").models(Foo).order_by("django_id")[0].text, "A different body") self.assertEqual(sqs.using("whoosh").models(Foo).order_by("django_id")[0].text, "foo 1")
def test_handle_delete(self): # Because the code here is pretty leaky (abstraction-wise), we'll test # the actual setup. # First, ensure the signal is setup. self.assertEqual(len(models.signals.post_delete.receivers), 1) # Second, check the existing search data. sqs = SearchQuerySet('solr') self.assertEqual(sqs.using('solr').count(), 5) self.assertEqual(sqs.using('solr').models(Foo).count(), 2) self.assertEqual(sqs.using('solr').models(Bar).count(), 3) self.assertEqual(sqs.using('whoosh').count(), 2) self.assertEqual(sqs.using('whoosh').models(Foo).count(), 2) self.assertEqual( sqs.using('solr').models(Foo).order_by('django_id')[0].text, 'foo 1') self.assertEqual( sqs.using('whoosh').models(Foo).order_by('django_id')[0].text, 'foo 1') # Third, delete the model, which should fire the signal & remove the # record from the index. self.foo_1.delete() # Fourth, check the search data for the now-removed data, making sure counts # have changed correctly. sqs = SearchQuerySet('solr') self.assertEqual(sqs.using('solr').count(), 4) self.assertEqual(sqs.using('solr').models(Foo).count(), 1) self.assertEqual(sqs.using('solr').models(Bar).count(), 3) self.assertEqual(sqs.using('whoosh').count(), 2) self.assertEqual(sqs.using('whoosh').models(Foo).count(), 2) self.assertEqual( sqs.using('solr').models(Foo).order_by('django_id')[0].text, 'foo 2') self.assertEqual( sqs.using('whoosh').models(Foo).order_by('django_id')[0].text, 'foo 1')
def test_handle_save(self): # Because the code here is pretty leaky (abstraction-wise), we'll test # the actual setup. # First, ensure the signal is setup. self.assertEqual(len(models.signals.post_save.receivers), 1) # Second, check the existing search data. sqs = SearchQuerySet('solr') self.assertEqual(sqs.using('solr').count(), 5) self.assertEqual(sqs.using('solr').models(Foo).count(), 2) self.assertEqual(sqs.using('solr').models(Bar).count(), 3) self.assertEqual(sqs.using('whoosh').count(), 2) self.assertEqual(sqs.using('whoosh').models(Foo).count(), 2) self.assertEqual( sqs.using('solr').models(Foo).order_by('django_id')[0].text, 'foo 1') self.assertEqual( sqs.using('whoosh').models(Foo).order_by('django_id')[0].text, 'foo 1') # Third, save the model, which should fire the signal & index the # new data. self.foo_1.body = 'A different body' self.foo_1.save() # Fourth, check the search data for the updated data, making sure counts # haven't changed. sqs = SearchQuerySet('solr') self.assertEqual(sqs.using('solr').count(), 5) self.assertEqual(sqs.using('solr').models(Foo).count(), 2) self.assertEqual(sqs.using('solr').models(Bar).count(), 3) self.assertEqual(sqs.using('whoosh').count(), 2) self.assertEqual(sqs.using('whoosh').models(Foo).count(), 2) self.assertEqual( sqs.using('solr').models(Foo).order_by('django_id')[0].text, 'A different body') self.assertEqual( sqs.using('whoosh').models(Foo).order_by('django_id')[0].text, 'foo 1')
def queryset(self, postdata, exclude=None): """ Build the `self.model` queryset limited to selected filters. """ if self.qs is None: #qs = self.model.objects.all().select_related() from haystack.query import SearchQuerySet qs = SearchQuerySet() if self.connection: qs = qs.using(self.connection) qs = qs.filter(indexed_model_name__in=[self.model.__name__], **self.global_filters) else: qs = self.qs filters = { } filters2 = [] # Then for each filter for option in self.options: # If filter is not excluded explicitly (used to get counts # for choices). if option == exclude: continue # If filter contains valid data, check jQuery style encoding of array params if option.field_name not in postdata and option.field_name+"[]" not in postdata: continue # Do filtering if option.filter is not None: qs_ = option.filter(qs, postdata) if isinstance(qs_, dict): filters.update(qs_) else: qs = qs_ else: values = postdata.getlist(option.field_name)+postdata.getlist(option.field_name+"[]") if option.type == "text": # For full-text searching, don't use __in so that the search # backend does its usual query operation. values = " ".join(values) # should not really be more than one, but in case the parameter is specified multiple times in the query string if self.qs is None: # This is a Haystack search. Handle text a little differently. # Wrap it in an AutoQuery so advanced search options like quoted phrases are used. # Query both text and text_boosted, which has a boost applied at the field level. from haystack.query import SQ from haystack.inputs import AutoQuery values = AutoQuery(values) filters2.append( SQ(text=values) | SQ(text_boosted=values) ) else: filters[option.orm_field_name] = values elif not u'__ALL__' in values: # if __ALL__ value presents in filter values # then do not limit queryset def parse_booleans(x): for y in x: if y in ("true", "on"): yield True elif y == "false": yield False else: yield y values = list(parse_booleans(values)) filters['%s__in' % option.orm_field_name] = values # apply filters simultaneously so that filters on related objects are applied # to the same related object. if they were applied chained (i.e. filter().filter()) # then they could apply to different objects. if len(filters) + len(filters2) > 0: qs = qs.filter(*filters2, **filters) for name, key, default, func in self.sort_options: if postdata.get("sort", "") == key: if not func: qs = qs.order_by(key) else: qs = func(qs) # Django ORM but not Haystack if hasattr(qs, 'distinct'): return qs.distinct() # Haystack but not Django ORM else: # Revise the SearchQuerySet to iterate over model objects # rather than SearchResult objects. class SR: def __init__(self, qs): self.qs = qs def facet(self, field, **kwargs): return self.qs.facet(field, **kwargs) def count(self): return len(self.qs) def order_by(self, field): return SR(self.qs.order_by(field)) def __len__(self): return len(self.qs) def __getitem__(self, index): # slices too return SR(self.qs[index]) def __iter__(self): for item in self.qs: yield item.object return SR(qs)