Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django Haystack - How to filter search results by a boolean field?

Trying to filter a SearchQuerySet by a boolean value doesn't work for me. (I am using the provided "Simple" backend search engine while testing.)

I have an index like:

class MyIndex(indexes.SearchIndex, indexes.Indexable):
    text = indexes.CharField(document=True, use_template=True)
    has_been_sent = indexes.BooleanField(model_attr='has_been_sent')
    # other fields

    def get_model(self):
        return MyModel

And I use a custom form for the search:

BOOLEAN_OPTIONS = [ ('either', 'Either'), ('yes', 'Yes'), ('no', 'No') ]

class MyModelSearchForm(SearchForm):
    # other fields
    has_been_sent = forms.ChoiceField( widget = forms.Select(), label = 'Sent?', choices=BOOLEAN_OPTIONS )

def search(self):
    sqs = super(MyModelSearchForm, self).search()

    if not self.is_valid(): return self.no_query_found()

    sqs = sqs.models(MyModel) # cuts out other models from the search results
    if self.cleaned_data['has_been_sent'] != 'either':
        if self.cleaned_data['has_been_sent'] == 'yes': sent = True
        else: sent = False
        sqs = sqs.filter(has_been_sent=sent)

    return sqs

If I set the has_been_sent option to Yes or No in the form, I always get 0 results, which is clearly wrong. I've also tried in the shell, with no luck. sqs.filter(has_been_sent=True) and sqs.filter(has_been_sent=False) both return an empty list, EVEN THOUGH sqs.values('has_been_sent') clearly shows a bunch of records with True values for has_been_sent. And even stranger, sqs.filter(has_been_sent='t') returns a subset of records, along with 'f', 'a', and unrelated letters like 'j'! I'm at a total loss. Does anybody have experience with this sort of problem with Haystack?

On a related note, are the fields you filter on through SearchQuerySet().filter() from the index fields (in search_indexes.py) or the model fields (in their respective models.py)?

EDIT:

I've been attempting to test my filters through Django's manage.py shell, but I think I'm doing it wrong. It doesn't seem to be following my search_indexes.py, since I limited it to a subset of MyModel with the index_queryset() method there, but I get ALL objects of MyModel in the shell.

>>> from haystack.query import SearchQuerySet
>>> from myapp.models import MyModel
>>> sqs = SearchQuerySet().models(MyModel)

And then some testing:

>>> len(sqs) # SHOULD be 5, due to the index_queryset() method I defined in search_indexes.py
17794
>>> sqs.filter(has_been_sent='true') # Same happens for True, 'TRUE', and all forms of false
[]
>>> len(sqs.filter(not_a_real_field='g')) # Made-up filter field, returns a subset somehow??
2591
>>> len(sqs.filter(has_been_sent='t'))
3621
>>> len(sqs.filter(has_been_sent='f'))
2812

Because I get a subset when filtering on the fake field, I don't think it's recognizing has_been_sent as one of my filter fields. Especially since the results for 't' and 'f' don't add up to the total, which it SHOULD, as that boolean field is required for all records. Am I missing a step in my testing?

like image 877
Goluxas Avatar asked Oct 07 '13 20:10

Goluxas


2 Answers

Try to filter as a string true or false in the query, this has been the known limitation in haystack and i am not sure if this is fixed, instead of doing:

sqs.filter(has_been_sent=True)

Do this:

sqs.filter(has_been_sent='true') # true or false in lowercase

P.S when you do SearchQuerySet().filter() you filter based on the fields defined in search_indexes.py file.

like image 113
Aamir Rind Avatar answered Nov 19 '22 23:11

Aamir Rind


It appears that the problem was in the Simple backend. I installed and switched Haystack over to Whoosh, and this problem cleared up. (Now SearchQuerySet().models() doesn't work, but that's apparently a documented bug with Haystack + Whoosh.)

Edit: Due to further troubles with Whoosh, I switched to using Solr 4.5.1 as my backend. Everything is working as expected now.

like image 25
Goluxas Avatar answered Nov 19 '22 23:11

Goluxas