user764357
user764357

Reputation:

Using "google-like" colon-search operator with Haystack

I'm developing a Django application with Haystack search engine capability, and am wanting to implement "google-like" colon search operators.

When doing a google search you can use queries like filetype:pdf or site:www.stackoverflow.com to restrict the specific search results, and would like to implement a similar style.

I know Solr, one of the search engines underneath Haystack can do this on specific fields but was curious if there was a generic approach in Haystack.

The current solution I am looking at building would be to take the search input from a form, use a regex search to find terms matching \b[a-z]+:[\w\d]+\b and then checking if they are appropriate fields to search on and using SearchQuerySet.filter to restrict results.

However, I am hoping there is an already in-built way to specify a afield in a SearchIndex can be used in this fashion automatically. Is this possible?

Upvotes: 1

Views: 356

Answers (2)

user764357
user764357

Reputation:

So this is actually quite easy, as Haystack provides a straight forward way to get all of the fields for the index using:

connections[DEFAULT_ALIAS].get_unified_index()

All that needs to be done is tokenize the search string, remove any that have a token that is a valid field, and run the search on the terms that are left before using the fields in a call to filter.

I've released it as a gist as well, but will probably end up packaging it at a later stage

from haystack.constants import DEFAULT_ALIAS
from haystack import connections
class TokenSearchForm(SearchForm):
    def prepare_tokens(self):
        try:
            query = self.cleaned_data.get('q')
        except:
            return {}
        opts = connections[DEFAULT_ALIAS].get_unified_index().fields.keys()
        kwargs = {}
        query_text = []
        for word in query.split(" "):
            if ":" in word:
                opt,arg = word.split(":",1)
                if opt in opts:
                    kwargs[str(opt)]=arg
            else:
                query_text.append(word)
        self.query_text = " ".join(query_text)
        self.kwargs = kwargs
        return kwargs

    def search(self):
        self.query_text = None
        kwargs = self.prepare_tokens()
        if not self.is_valid():
            return self.no_query_found()

        if not self.cleaned_data.get('q'):
            return self.no_query_found()

        sqs = self.searchqueryset.auto_query(self.query_text)

        if kwargs:
            sqs = sqs.filter(**kwargs)

        if self.load_all:
            sqs = sqs.load_all()

        return sqs

Upvotes: 1

Ed Murphy
Ed Murphy

Reputation: 11

you can take advantage of the the ability to expand dictionaries into keyword arguments

after applying your regex to filter you search field and search value create a dictionary like

my_search = {'search_field':'search_value'}
search_res = SearchQuerySet().filter(**my_search)

just tested it out in my implementation and worked great! Unless you are worried about users searching fields that they should not be searching/viewing there is no need to verify that it is a valid search field, an invalid search filed will just return no results

Upvotes: 1

Related Questions