Basic Usage

Usage is best demonstrated with some simple examples.

Warning

The code here is for demonstration purposes only! It might work (or not, I haven’t tested), but as always, don’t blindly copy code from the internet.

Examples

models.py

Let’s say we have an app which contains a model Location. It could look something like this.

#
# models.py
#

from django.db import models
from haystack.utils.geo import Point


class Location(models.Model):

    latitude = models.FloatField()
    longitude = models.FloatField()
    address = models.CharField(max_length=100)
    city = models.CharField(max_length=30)
    zip_code = models.CharField(max_length=10)

    created = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.address

    @property
    def coordinates(self):
        return Point(self.longitude, self.latitude)

search_indexes.py

We would have to make a search_indexes.py file for haystack to pick it up.

#
# search_indexes.py
#

from django.utils import timezone
from haystack import indexes
from .models import Location


class LocationIndex(indexes.SearchIndex, indexes.Indexable):

    text = indexes.CharField(document=True, use_template=True)
    address = indexes.CharField(model_attr="address")
    city = indexes.CharField(model_attr="city")
    zip_code = indexes.CharField(model_attr="zip_code")

    autocomplete = indexes.EdgeNgramField()
    coordinates = indexes.LocationField(model_attr="coordinates")

    @staticmethod
    def prepare_autocomplete(obj):
        return " ".join((
            obj.address, obj.city, obj.zip_code
        ))

    def get_model(self):
        return Location

    def index_queryset(self, using=None):
        return self.get_model().objects.filter(
            created__lte=timezone.now()
        )

views.py

For a generic Django REST Framework view, you could do something like this.

#
# views.py
#

from drf_haystack.serializers import HaystackSerializer
from drf_haystack.viewsets import HaystackViewSet

from .models import Location
from .search_indexes import LocationIndex


class LocationSerializer(HaystackSerializer):

    class Meta:
        # The `index_classes` attribute is a list of which search indexes
        # we want to include in the search.
        index_classes = [LocationIndex]

        # The `fields` contains all the fields we want to include.
        # NOTE: Make sure you don't confuse these with model attributes. These
        # fields belong to the search index!
        fields = [
            "text", "address", "city", "zip_code", "autocomplete"
        ]


class LocationSearchView(HaystackViewSet):

    # `index_models` is an optional list of which models you would like to include
    # in the search result. You might have several models indexed, and this provides
    # a way to filter out those of no interest for this particular view.
    # (Translates to `SearchQuerySet().models(*index_models)` behind the scenes.
    index_models = [Location]

    serializer_class = LocationSerializer

urls.py

Finally, hook up the views in your urls.py file.

Note

Make sure you specify the base_name attribute when wiring up the view in the router. Since we don’t have any single model for the view, it is impossible for the router to automatically figure out the base name for the view.

#
# urls.py
#

from django.conf.urls import patterns, url, include
from rest_framework import routers

from .views import LocationSearchView

router = routers.DefaultRouter()
router.register("location/search", LocationSearchView, base_name="location-search")


urlpatterns = patterns(
    "",
    url(r"/api/v1/", include(router.urls)),
)

Query time!

Now that we have a view wired up, we can start using it. By default, the HaystackViewSet (which, more importantly inherits the HaystackGenericAPIView class) is set up to use the HaystackFilter. This is the most basic filter included and can do basic search by querying any of the field included in the fields attribute on the Serializer.

http://example.com/api/v1/location/search/?city=Oslo

Would perform a query looking up all documents where the city field equals “Oslo”.

Field Lookups

You can also use field lookups in your field queries. See the Haystack field lookups documentation for info on what lookups are available. A query using a lookup might look like the following:

http://example.com/api/v1/location/search/?city__startswith=Os

This would perform a query looking up all documents where the city field started with “Os”. You might get “Oslo”, “Osaka”, and “Ostrava”.

Term Negation

You can also specify terms to exclude from the search results using the negation keyword. The default keyword is not, but is configurable via settings using DRF_HAYSTACK_NEGATION_KEYWORD.

http://example.com/api/v1/location/search/?city__not=Oslo
http://example.com/api/v1/location/search/?city__not__contains=Los
http://example.com/api/v1/location/search/?city__contains=Los&city__not__contains=Angeles