Input Types

Input types allow you to specify more advanced query behavior. They serve as a way to alter the query, often in backend-specific ways, without altering your Python code; as well as enabling use of more advanced features.

Input types currently are only useful with the filter/exclude methods on SearchQuerySet. Expanding this support to other methods is on the roadmap.

Available Input Types

Included with Haystack are the following input types:

Raw

class haystack.inputs.Raw

Raw allows you to specify backend-specific query syntax. If Haystack doesn’t provide a way to access special query functionality, you can make use of this input type to pass it along.

Example:

# Fielded.
sqs = SearchQuerySet().filter(author=Raw('daniel OR jones'))

# Non-fielded.
# See ``AltParser`` for a better way to construct this.
sqs = SearchQuerySet().filter(content=Raw('{!dismax qf=author mm=1}haystack'))

Clean

class haystack.inputs.Clean

Clean takes standard user (untrusted) input and sanitizes it. It ensures that no unintended operators or special characters make it into the query.

This is roughly analogous to Django’s autoescape support.

Note

By default, if you hand a SearchQuerySet a bare string, it will get wrapped in this class.

Example:

# This becomes "daniel or jones".
sqs = SearchQuerySet().filter(content=Clean('daniel OR jones'))

# Things like ``:`` & ``/`` get escaped.
sqs = SearchQuerySet().filter(url=Clean('http://www.example.com'))

# Equivalent (automatically wrapped in ``Clean``).
sqs = SearchQuerySet().filter(url='http://www.example.com')

Exact

class haystack.inputs.Exact

Exact allows for making sure a phrase is exactly matched, unlike the usual AND lookups, where words may be far apart.

Example:

sqs = SearchQuerySet().filter(author=Exact('n-gram support'))

# Equivalent.
sqs = SearchQuerySet().filter(author__exact='n-gram support')

Not

class haystack.inputs.Not

Not allows negation of the query fragment it wraps. As Not is a subclass of Clean, it will also sanitize the query.

This is generally only used internally. Most people prefer to use the SearchQuerySet.exclude method.

Example:

sqs = SearchQuerySet().filter(author=Not('daniel'))

AutoQuery

class haystack.inputs.AutoQuery

AutoQuery takes a more complex user query (that includes simple, standard query syntax bits) & forms a proper query out of them. It also handles sanitizing that query using Clean to ensure the query doesn’t break.

AutoQuery accommodates for handling regular words, NOT-ing words & extracting exact phrases.

Example:

# Against the main text field with an accidental ":" before "search".
# Generates a query like ``haystack (NOT whoosh) "fast search"``
sqs = SearchQuerySet().filter(content=AutoQuery('haystack -whoosh "fast :search"'))

# Equivalent.
sqs = SearchQuerySet().auto_query('haystack -whoosh "fast :search"')

# Fielded.
sqs = SearchQuerySet().filter(author=AutoQuery('daniel -day -lewis'))

AltParser

class haystack.inputs.AltParser

AltParser lets you specify that a portion of the query should use a separate parser in the search engine. This is search-engine-specific, so it may decrease the portability of your app.

Currently only supported under Solr.

Example:

# DisMax.
sqs = SearchQuerySet().filter(content=AltParser('dismax', 'haystack', qf='text', mm=1))

# Prior to the spatial support, you could do...
sqs = SearchQuerySet().filter(content=AltParser('dismax', 'haystack', qf='author', mm=1))

Creating Your Own Input Types

Building your own input type is relatively simple. All input types are simple classes that provide an __init__ & a prepare method.

The __init__ may accept any args/kwargs, though the typical use usually just involves a query string.

The prepare method lets you alter the query the user provided before it becomes of the main query. It is lazy, called as late as possible, right before the final query is built & shipped to the engine.

A full, if somewhat silly, example looks like:

from haystack.inputs import Clean


class NoShoutCaps(Clean):
    input_type_name = 'no_shout_caps'
    # This is the default & doesn't need to be specified.
    post_process = True

    def __init__(self, query_string, **kwargs):
        # Stash the original, if you need it.
        self.original = query_string
        super().__init__(query_string, **kwargs)

    def prepare(self, query_obj):
        # We need a reference to the current ``SearchQuery`` object this
        # will run against, in case we need backend-specific code.
        query_string = super().prepare(query_obj)

        # Take that, capital letters!
        return query_string.lower()