Elasticsearch Configuration
Elasticsearch is a distributed, RESTful search and analytics engine built on Apache Lucene. Before proceeding, ensure the Java Runtime Environment (JRE) is installed and the JAVA_HOME environment variable is configured correctly. Once the prerequisites are met, download the Elasticsearch distribution and start the service by executing the startup script located in the bin directory.
elasticsearch.bat
Django Haystack Integration
Haystack provides a modular search layer for Django, enabling developers to utilize various search backends such as Elasticsearch, Solr, or Whoosh without altering application code. To integrate Haystack with a Django REST Framework (DRF) project, install the necessary packages. If the project utilizes DRF, drf-haystack is the preferred library.
pip install drf-haystack
pip install elasticsearch==2.4.1
Settings Configuration
Modify the Django settings file to include Haystack in the installed applications.
INSTALLED_APPS = [
...
'haystack',
...
]
Configure the search backend connection. The default Elasticsearch port is 9200. Additionally, configuring the RealtimeSignalProcessor ensures that indexes are updated automatically when data changes.
HAYSTACK_CONNECTIONS = {
'default': {
'ENGINE': 'haystack.backends.elasticsearch_backend.ElasticsearchSearchEngine',
'URL': 'http://127.0.0.1:9200/',
'INDEX_NAME': 'project_index',
},
}
HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor'
Creating Search Indexes
To define which model fields are searchable, create a search_indexes.py file inside the relevant application directory. This file acts as the bridge between the database models and the search engine.
# products/search_indexes.py
from haystack import indexes
from .models import Product
class ProductIndex(indexes.SearchIndex, indexes.Indexable):
text = indexes.CharField(document=True, use_template=True)
def get_model(self):
return Product
def index_queryset(self, using=None):
return self.get_model().objects.all()
The text field, marked with document=True, serves as the primary field for keyword queries. The content for this field is defined in a separate template.
Index Data Template
Create a template file at templates/search/indexes/products/product_text.txt. This template dictates which model attributes are combined to form the searchable text index.
{{ object.name }}
{{ object.description }}
{{ object.id }}
After defining the index and template, generate the initial index structure:
python manage.py rebuild_index
Serializers and Views
Create a serializer to handle the search results. The HaystackSerializer wraps the search results, while a standard ModelSerializer handles the nested object data.
# products/serializers.py
from rest_framework import serializers
from drf_haystack.serializers import HaystackSerializer
from .models import Product
from .search_indexes import ProductIndex
class ProductModelSerializer(serializers.ModelSerializer):
class Meta:
model = Product
fields = ('id', 'name', 'price', 'stock')
class ProductSearchSerializer(HaystackSerializer):
object = ProductModelSerializer(read_only=True)
class Meta:
index_classes = [ProductIndex]
fields = ('text', 'object', 'id', 'name')
Implement the view using the HaystackViewSet, which provides default search functionality.
# products/views.py
from drf_haystack.viewsets import HaystackViewSet
from .serializers import ProductSearchSerializer
from .models import Product
class ProductSearchViewSet(HaystackViewSet):
index_models = [Product]
serializer_class = ProductSearchSerializer
Routing and Verification
Register the viewset in the URL configuration.
from rest_framework.routers import DefaultRouter
from products.views import ProductSearchViewSet
router = DefaultRouter()
router.register(r'search', ProductSearchViewSet, basename='product_search')
urlpatterns += router.urls
Search queries can be performed by passing the text parameter to the endpoint, for example: /search/?text=keyword. If a compatibility error regarding _get_count occurs in older DRF versions, patching the pagination module with a custom count function may be necessary to resolve the attribute error.