.. _ref-migration_from_1_to_2:

===========================================
Migrating From Haystack 1.X to Haystack 2.X
===========================================

Haystack introduced several backward-incompatible changes in the process of
moving from the 1.X series to the 2.X series. These were done to clean up the
API, to support new features & to clean up problems in 1.X. At a high level,
they consisted of:

* The removal of ``SearchSite`` & ``haystack.site``.
* The removal of ``handle_registrations`` & ``autodiscover``.
* The addition of multiple index support.
* The removal/renaming of various settings.

This guide will help you make the changes needed to be compatible with Haystack
2.X.


Settings
========

Most prominently, the old way of specifying a backend & its settings has changed
to support the multiple index feature. A complete Haystack 1.X example might
look like::

    HAYSTACK_SEARCH_ENGINE = 'solr'
    HAYSTACK_SOLR_URL = 'http://localhost:9001/solr/default'
    HAYSTACK_SOLR_TIMEOUT = 60 * 5
    HAYSTACK_INCLUDE_SPELLING = True
    HAYSTACK_BATCH_SIZE = 100
    
    # Or...
    HAYSTACK_SEARCH_ENGINE = 'whoosh'
    HAYSTACK_WHOOSH_PATH = '/home/search/whoosh_index'
    HAYSTACK_WHOOSH_STORAGE = 'file'
    HAYSTACK_WHOOSH_POST_LIMIT = 128 * 1024 * 1024
    HAYSTACK_INCLUDE_SPELLING = True
    HAYSTACK_BATCH_SIZE = 100
    
    # Or...
    HAYSTACK_SEARCH_ENGINE = 'xapian'
    HAYSTACK_XAPIAN_PATH = '/home/search/xapian_index'
    HAYSTACK_INCLUDE_SPELLING = True
    HAYSTACK_BATCH_SIZE = 100

In Haystack 2.X, you can now supply as many backends as you like, so all of the
above settings can now be active at the same time. A translated set of settings
would look like::

    HAYSTACK_CONNECTIONS = {
        'default': {
            'ENGINE': 'haystack.backends.solr_backend.SolrEngine',
            'URL': 'http://localhost:9001/solr/default',
            'TIMEOUT': 60 * 5,
            'INCLUDE_SPELLING': True,
            'BATCH_SIZE': 100,
        },
        'autocomplete': {
            'ENGINE': 'haystack.backends.whoosh_backend.WhooshEngine',
            'PATH': '/home/search/whoosh_index',
            'STORAGE': 'file',
            'POST_LIMIT': 128 * 1024 * 1024,
            'INCLUDE_SPELLING': True,
            'BATCH_SIZE': 100,
        },
        'slave': {
            'ENGINE': 'xapian_backend.XapianEngine',
            'PATH': '/home/search/xapian_index',
            'INCLUDE_SPELLING': True,
            'BATCH_SIZE': 100,
        },
    }

You are required to have at least one connection listed within
``HAYSTACK_CONNECTIONS``, it must be named ``default`` & it must have a valid
``ENGINE`` within it. Bare minimum looks like::

    HAYSTACK_CONNECTIONS = {
        'default': {
            'ENGINE': 'haystack.backends.simple_backend.SimpleEngine'
        }
    }

The key for each backend is an identifier you use to describe the backend within
your app. You should refer to the :ref:`ref-multiple_index` documentation for
more information on using the new multiple indexes & routing features.

Also note that the ``ENGINE`` setting has changed from a lowercase "short name"
of the engine to a full path to a new ``Engine`` class within the backend.
Available options are:

* ``haystack.backends.solr_backend.SolrEngine``
* ``haystack.backends.whoosh_backend.WhooshEngine``
* ``haystack.backends.simple_backend.SimpleEngine``

Additionally, the following settings were outright removed & will generate
an exception if found:

* ``HAYSTACK_SITECONF`` - Remove this setting & the file it pointed to.
* ``HAYSTACK_ENABLE_REGISTRATIONS``
* ``HAYSTACK_INCLUDE_SPELLING``


Backends
========

The ``dummy`` backend was outright removed from Haystack, as it served very
little use after the ``simple`` (pure-ORM-powered) backend was introduced.

If you wrote a custom backend, please refer to the "Custom Backends" section
below.


Indexes
=======

The other major changes affect the ``SearchIndex`` class. As the concept of
``haystack.site`` & ``SearchSite`` are gone, you'll need to modify your indexes.

A Haystack 1.X index might've looked like::

    import datetime
    from haystack.indexes import *
    from haystack import site
    from myapp.models import Note
    
    
    class NoteIndex(SearchIndex):
        text = CharField(document=True, use_template=True)
        author = CharField(model_attr='user')
        pub_date = DateTimeField(model_attr='pub_date')
        
        def get_queryset(self):
            """Used when the entire index for model is updated."""
            return Note.objects.filter(pub_date__lte=datetime.datetime.now())
    
    
    site.register(Note, NoteIndex)

A converted Haystack 2.X index should look like::

    import datetime
    from haystack import indexes
    from myapp.models import Note
    
    
    class NoteIndex(indexes.SearchIndex):
        text = indexes.CharField(document=True, use_template=True)
        author = indexes.CharField(model_attr='user')
        pub_date = indexes.DateTimeField(model_attr='pub_date')
        
        def get_model(self):
            return Note
        
        def index_queryset(self):
            """Used when the entire index for model is updated."""
            return self.get_model().objects.filter(pub_date__lte=datetime.datetime.now())

Note the import on ``site`` & the registration statements are gone. Newly added
are is the ``NoteIndex.get_model`` method. This is a **required** method &
should simply return the ``Model`` class the index is for.

Additionally, the name of the ``document=True`` field is now enforced to be
``text`` across all indexes. If you need it named something else, you should
set the ``HAYSTACK_DOCUMENT_FIELD`` setting. For example::

    HAYSTACK_DOCUMENT_FIELD = 'pink_polka_dot'

Finally, the ``index_queryset`` method should supplant the ``get_queryset``
method. This was present in the Haystack 1.2.X series & ``get_queryset`` will
continue to work for backward-compatibility.

Finally, if you were unregistering other indexes before, you should make use of
the new ``HAYSTACK_EXCLUDED_INDEXES`` setting. It should be a list of strings
that contain the Python import path to the indexes that should not be loaded &
used. For example::

    HAYSTACK_EXCLUDED_INDEXES = [
        # Imagine that these indexes exist. They don't.
        'django.contrib.auth.search_indexes.UserIndex',
        'third_party_blog_app.search_indexes.EntryIndex',
    ]

This allows for reliable swapping of the index that handles a model without
relying on correct import order.


Done!
=====

For most basic uses of Haystack, this is all that is necessary to work with
Haystack 2.X. You should rebuild your index if needed & test your new setup.


Advanced Uses
=============

Swapping Backend
----------------

If you were manually swapping the ``SearchQuery`` or ``SearchBackend`` being
used by ``SearchQuerySet`` in the past, it's now preferable to simply setup
another connection & use the ``SearchQuerySet.using`` method to select that
connection instead.

Also, if you were manually instantiating ``SearchBackend`` or ``SearchQuery``,
it's now preferable to rely on the connection's engine to return the right
thing. For example::

    from haystack import connections
    backend = connections['default'].get_backend()
    query = connections['default'].get_query()


Custom Backends
---------------

If you had written a custom ``SearchBackend`` and/or custom ``SearchQuery``,
there's a little more work needed to be Haystack 2.X compatible.

You should, but don't have to, rename your ``SearchBackend`` & ``SearchQuery``
classes to be more descriptive/less collide-y. For example,
``solr_backend.SearchBackend`` became ``solr_backend.SolrSearchBackend``. This
prevents non-namespaced imports from stomping on each other.

You need to add a new class to your backend, subclassing ``BaseEngine``. This
allows specifying what ``backend`` & ``query`` should be used on a connection
with less duplication/naming trickery. It goes at the bottom of the file (so
that the classes are defined above it) and should look like::

    from haystack.backends import BaseEngine
    from haystack.backends.solr_backend import SolrSearchQuery
    
    # Code then...
    
    class MyCustomSolrEngine(BaseEngine):
        # Use our custom backend.
        backend = MySolrBackend
        # Use the built-in Solr query.
        query = SolrSearchQuery

Your ``HAYSTACK_CONNECTIONS['default']['ENGINE']`` should then point to the
full Python import path to your new ``BaseEngine`` subclass.

Finally, you will likely have to adjust the ``SearchBackend.__init__`` &
``SearchQuery.__init__``, as they have changed significantly. Please refer to
the commits for those backends.
