Usage is best demonstrated with some simple examples.
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.
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)
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() )
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
Finally, hook up the views in your urls.py file.
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)), )
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.
Would perform a query looking up all documents where the city field equals “Oslo”.
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:
This would perform a query looking up all documents where the city field started with “Os”. You might get “Oslo”, “Osaka”, and “Ostrava”.
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
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