Reputation:
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
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
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