Multiple search indexes¶
So far, we have only used one class in the index_classes
attribute of our serializers. However, you are able to specify
a list of them. This can be useful when your search engine has indexed multiple models and you want to provide aggregate
results across two or more of them. To use the default multiple index support, simply add multiple indexes the index_classes
list
class PersonIndex(indexes.SearchIndex, indexes.Indexable):
text = indexes.CharField(document=True, use_template=True)
firstname = indexes.CharField(model_attr="first_name")
lastname = indexes.CharField(model_attr="last_name")
def get_model(self):
return Person
class PlaceIndex(indexes.SearchIndex, indexes.Indexable):
text = indexes.CharField(document=True, use_template=True)
address = indexes.CharField(model_attr="address")
def get_model(self):
return Place
class ThingIndex(indexes.SearchIndex, indexes.Indexable):
text = indexes.CharField(document=True, use_template=True)
name = indexes.CharField(model_attr="name")
def get_model(self):
return Thing
class AggregateSerializer(HaystackSerializer):
class Meta:
index_classes = [PersonIndex, PlaceIndex, ThingIndex]
fields = ["firstname", "lastname", "address", "name"]
class AggregateSearchViewSet(HaystackViewSet):
serializer_class = AggregateSerializer
Note
The AggregateSearchViewSet
class above omits the optional index_models
attribute. This way results from all the
models are returned.
The result from searches using multiple indexes is a list of objects, each of which contains only the fields appropriate to the model from which the result came. For instance if a search returned a list containing one each of the above models, it might look like the following:
[
{
"text": "John Doe",
"firstname": "John",
"lastname": "Doe"
},
{
"text": "123 Doe Street",
"address": "123 Doe Street"
},
{
"text": "Doe",
"name": "Doe"
}
]
Declared fields¶
You can include field declarations in the serializer class like normal. Depending on how they are named, they will be treated as common fields and added to every result or as specific to results from a particular index.
Common fields are declared as you would any serializer field. Index-specific fields must be prefixed with “_<index class name>__”. The following example illustrates this usage:
class AggregateSerializer(HaystackSerializer):
extra = serializers.CharField()
_ThingIndex__number = serializers.IntegerField()
class Meta:
index_classes = [PersonIndex, PlaceIndex, ThingIndex]
fields = ["firstname", "lastname", "address", "name"]
def get_extra(self):
return "whatever"
def get__ThingIndex__number(self):
return 42
The results of a search might then look like the following:
[
{
"text": "John Doe",
"firstname": "John",
"lastname": "Doe",
"extra": "whatever"
},
{
"text": "123 Doe Street",
"address": "123 Doe Street",
"extra": "whatever"
},
{
"text": "Doe",
"name": "Doe",
"extra": "whatever",
"number": 42
}
]
Multiple Serializers¶
Alternatively, you can specify a ‘serializers’ attribute on your Meta class to use a different serializer class for different indexes as show below:
class AggregateSearchSerializer(HaystackSerializer):
class Meta:
serializers = {
PersonIndex: PersonSearchSerializer,
PlaceIndex: PlaceSearchSerializer,
ThingIndex: ThingSearchSerializer
}
The serializers
attribute is the important thing here, It’s a dictionary with SearchIndex
classes as
keys and Serializer
classes as values. Each result in the list of results from a search that contained
items from multiple indexes would be serialized according to the appropriate serializer.
Warning
If a field name is shared across serializers, and one serializer overrides the field mapping, the overridden mapping will be used for all serializers. See the example below for more details.
from rest_framework import serializers
class PersonSearchSerializer(HaystackSerializer):
# NOTE: This override will be used for both Person and Place objects.
name = serializers.SerializerMethodField()
class Meta:
fields = ['name']
class PlaceSearchSerializer(HaystackSerializer):
class Meta:
fields = ['name']
class AggregateSearchSerializer(HaystackSerializer):
class Meta:
serializers = {
PersonIndex: PersonSearchSerializer,
PlaceIndex: PlaceSearchSerializer,
ThingIndex: ThingSearchSerializer
}