skip to navigation
skip to content

Not Logged In

elasticstack 0.2.0

Configurable indexing and other extras for Haystack (with ElasticSearch biases).


.. image::

.. image::

.. image::

:Version: 0.2.0
:Author: Ben Lopatin (

Configurable indexing and other extras for Haystack (with ElasticSearch

Full documentation is on `Read the Docs <http:"" en="" latest=""/>`_.


* `Django <https:""/>`_: tested against Django 1.4, 1.8, and 1.9
* `Haystack <http:""/>`_: tested against Haystack 2.4.0,
it should work with any combination of Haystack and Django that work
* `ElasticSearch <http:""/>`_: presumably any newish
version will do, however the only version tested against so far is 0.19.x

Features and goals

Some of these features are backend agnostic but most features have
ElasticSearch in mind.

For more background see the blog post `Stretching Haystack's ElasticSearch Backend <http:"" blog="" custom-haystack-elasticsearch-backend=""/>`_.

Configurable index mapping

The search mapping provided by Haystack's ElasticSearch backend includes brief
but sensible defaults for nGram analysis. You can add change these settings or
add your own mappings by providing a mapping dictionary using
`ELASTICSEARCH_INDEX_SETTINGS` in your settings file. This example takes the
default mapping and adds a synonym analyzer::

'settings': {
"analysis": {
"analyzer": {
"synonym_analyzer" : {
"type": "custom",
"tokenizer" : "standard",
"filter" : ["synonym"]
"ngram_analyzer": {
"type": "custom",
"tokenizer": "lowercase",
"filter": ["haystack_ngram", "synonym"]
"edgengram_analyzer": {
"type": "custom",
"tokenizer": "lowercase",
"filter": ["haystack_edgengram"]
"tokenizer": {
"haystack_ngram_tokenizer": {
"type": "nGram",
"min_gram": 3,
"max_gram": 15,
"haystack_edgengram_tokenizer": {
"type": "edgeNGram",
"min_gram": 2,
"max_gram": 15,
"side": "front"
"filter": {
"haystack_ngram": {
"type": "nGram",
"min_gram": 3,
"max_gram": 15
"haystack_edgengram": {
"type": "edgeNGram",
"min_gram": 2,
"max_gram": 15
"synonym" : {
"type" : "synonym",
"ignore_case": "true",
"synonyms_path" : "synonyms.txt"

The synonym filter is ready for your index, but will go unused yet.

Before your new analyzer can be used you will need to change your Haystack engine and rebuild/update
your index. In your `` modify `HAYSTACK_CONNECTIONS` accordingly::

'default': {
'ENGINE': 'elasticstack.backends.ConfigurableElasticSearchEngine',
'URL': env_var('HAYSTACK_URL', ''),
'INDEX_NAME': 'haystack',

The default analyzer for non-nGram fields in Haystack's ElasticSearch backend
is the `snowball analyzer <http:"" guide="" reference="" index-modules="" analysis="" snowball-analyzer.html="">`_.
A perfectly good analyzer but not necessarily what you need. It's also language
specific (English by default).

Specify your analyzer with `ELASTICSEARCH_DEFAULT_ANALYZER` in your settings


Now all your analyzed fields, except for nGram fields, will be analyzed using

Field based analysis

Even with a new default analyzer you may want to change this on a field by
field basis as fits your needs. To do so, use the fields from
`elasticstack.fields` to specify your analyzer with the `analyzer` keyword

from haystack import indexes
from elasticstack.fields import CharField
from myapp.models import MyContent

class MyContentIndex(indexes.SearchIndex, indexes.Indexable):
text = CharField(document=True, use_template=True,

def get_model(self):
return MyContent

Django CBV style views

Haystacks's class based views predate the inclusion of CBVs into the Django
core and so the paradigms are different. This makes it harder to impossible to
make use of view mixins.

The bundled `SearchView` and `FacetedSearchView` classes are based on
`django.views.generic.edit.FormView` using the `SearchMixin` and
`FacetedSearchMixin`, respectively. The `SearchMixin` provides the necessary
search related attributes and overloads the form processing methods to execute
the search.

The `SearchMixin` adds a few search specific attributes:

* `load_all` - a Boolean value for `specifying database lookups <http:"" en="" latest="" searchqueryset_api.html#load-all="">`_
* `queryset` - a default `SearchQuerySet`. Defaults to `EmtpySearchQuerySet`
* `search_field` - the name of the form field used for the query. This is added
to allow for views which may have more than one search form. Defaults to `q`.

.. note::
The `SearchMixin` uses the attribute named `queryset` for the resultant
`SearchQuerySet`. Naming this attribute `searchqueryset` would make more
sense semantically and hew closer to Haystack's naming convention, however
by using the `queryset` attribute shared by other Django view mixins it is
relatively easy to combine search functionality with other mixins and

Management commands


Make a change and wonder why your results don't look as expected? The
management command `show_mapping` will print the current mapping for
your defined search index(es). At the least it may show that you've simply
forgotten to update your index with new mappings::

python show_mapping

By default this will display the `existing_mapping` which shows the index,
document type, and document properties.::

"haystack": {
"modelresult": {
"properties": {
"is_active": {
"type": "boolean"
"text": {
"type": "string"
"published": {
"type": "date",
"format": "dateOptionalTime"

If you provide the `--detail` flag this will return only the field mappings but
including additional details, such as boost levels and field-specific

"is_active": {
"index": "not_analyzed",
"boost": 1,
"store": "yes",
"type": "boolean"
"text": {
"index": "analyzed",
"term_vector": "with_positions_offsets",
"type": "string",
"analyzer": "custom_analyzer",
"boost": 1,
"store": "yes"
"pub_date": {
"index": "analyzed",
"boost": 1,
"store": "yes",
"type": "date"


Provided the name of an indexed model and a key it generates and prints the
generated document for this object::

python show_document myapp.MyModel 19181

The JSON document will be formatted with 'pretty' indenting.

Stability, docs, and tests

The form, view, and backend functionality in this project is considered stable.
Test coverage is not substantial, but is run against Django 1.4 through Django
1.6 on Python 2.6 and Python 2.7, Django 1.5 and Django 1.6 on Python 3.3, and
Django 1.6 on PyPy.

Why not add this stuff to Haystack?

This project first aims to solve problems related specifically to working with
ElasticSearch. Haystack is 1) backend agnostic (a good thing), 2) needs to
support existing codebases, and 3) not my project. Most importantly, adding
these features through a separate Django app means providing them without
needing to fork Haystack. Hopefully some of the features here, once finalized
and tested, will be suitable to add to Haystack.


0.2.0 (2015-09-29)

* Switch to py.test
* Tests against Django 1.8, 1.9
* Drop pyelasticsearch requirement for installation

0.1.1 (2015-01-13)

* Bug fix in show_document management command

0.1.0 (2014-11-24)

* Major structural changes
* Bugfix for configurable search fields

0.0.6 (2013-10-04)

* Require pyelasticsearch for installation

0.0.5 (2013-09-28)

* Fixed reference to old method

0.0.4 (2013-09-28)

* Search form can search using specified field name
* Added management command to output mapping for an individual

0.0.3 (2013-09-28)

* Added default analyzer setting

0.0.2 (2013-09-28)

* Packaging fix

0.0.1 (2013-09-28)

* Initial release  
File Type Py Version Uploaded on Size
elasticstack-0.2.0-py2-none-any.whl (md5) Python Wheel 2.7 2015-09-30 21KB
elasticstack-0.2.0.tar.gz (md5) Source 2015-09-30 11KB
  • Downloads (All Versions):
  • 42 downloads in the last day
  • 333 downloads in the last week
  • 1320 downloads in the last month