Elasticsearch search only by one field

I have app on drf and i added elasticsearch(django_elasticsearch_dsl) for searching. But i faced with problem, when i want to search on two or more fields, elastic searching only by one field.

I'm trying to search by both 'title' and 'description', but it only searches in the 'title' field.

Django model

class Product(TimestampMixin, HistoricalModel, models.Model):
    class Status(models.TextChoices):
        NEW = 'new', _('New')
        USED = 'used', _('Used')

    class PublicationStatus(models.TextChoices):
        ACTIVE = 'active', _('Active')
        INACTIVE = 'inactive', _('Inactive')
        DRAFT = 'draft', _('Draft')  # Очікуючі
        REJECTED = 'rejected', _('Rejected')  # Відхилені
        SOLD = 'sold', _('Sold')
        DELETED = 'deleted', _('Deleted')

    title = models.CharField(_('Product name'), max_length=255)
    description = models.TextField(
        verbose_name=_('Product description'),
        validators=[
            MinLengthValidator(settings.MIN_LENGTH_DESCRIPTION, message=_('Product description is too short')),
            MaxLengthValidator(settings.MAX_LENGTH_DESCRIPTION, message=_('Product description is too long')),
        ],
    )
   ...

Document

@registry.register_document
class ProductDocument(Document):
    title = fields.TextField(analyzer="multilang_analyzer")
    description = fields.TextField(analyzer="multilang_analyzer")

    class Index:
        name = "products"
        settings = {
            'number_of_shards': 1,
            'number_of_replicas': 1,
            'analysis': {
                'analyzer': {
                    'multilang_analyzer': {
                        'type': 'custom',
                        'tokenizer': 'standard',
                        'char_filter': ['html_strip'],  
                        'filter': [
                            'lowercase',
                            'russian_stop',
                            'ukrainian_stop',
                            'english_stop',
                            'russian_stemmer',
                            'english_stemmer'
                        ]
                    }
                },
                'filter': {
                    'russian_stop': {'type': 'stop', 'stopwords': '_russian_'},
                    'ukrainian_stop': {'type': 'stop', 'stopwords': '_ukrainian_'},
                    'english_stop': {'type': 'stop', 'stopwords': '_english_'},
                    'russian_stemmer': {'type': 'stemmer', 'language': 'russian'},
                    'english_stemmer': {'type': 'stemmer', 'language': 'english'}
                }
            }
        }

    class Django:
        model = Product
        fields = ['title', 'description']

view

class SearchProductView(ListAPIView):
    serializer_class=ProductSerializer

    def get_queryset(self):
        q = self.request.GET.get("query", "")
        if q:
            search = (ProductDocument.search()
                      .query('multi-match', query=q, fields=["title^2", "description"]))
            response = search.execute()
            return response

serializer

class ProductSerializer(serializers.ModelSerializer):
    category = CategorySerializer(read_only=True)
    category_id = serializers.PrimaryKeyRelatedField(queryset=Category.objects.all(), write_only=True, required=True)
    prices = PriceSerializer(many=True, required=False, read_only=True)
    amount = serializers.DecimalField(
        max_digits=13,
        decimal_places=2,
        min_value=MIN_PRICE,
        write_only=True,
    )
    currency = serializers.PrimaryKeyRelatedField(queryset=Currency.objects.all(), write_only=True)
    images = ProductImageSerializer(many=True, source='product_images', read_only=True)
    uploaded_images = serializers.ListField(
        child=serializers.ImageField(use_url=True, allow_null=True),
        write_only=True,
        required=False,
        allow_empty=True,
    )
    video = ProductVideoSerializer(source='product_videos', read_only=True, many=True)
    upload_video = serializers.FileField(write_only=True, required=False)
    seller = serializers.SerializerMethodField(read_only=True)
    status = serializers.ChoiceField(choices=Product.Status.choices)

For example i have instance with these data

{"**id**": 4, 
"**title**": "Нові стоїки 52 тижні для наповненого життя", 
"description": "Книга ''Нові стоїки. 52 тижні для наповненого життя''. Прочитана 1 раз, стан нової. Можна забрати у Львові на Зеленій.\r\n\r\nКнижка про філософію стоїцизму — вчення, ... .", 
 ...

curl -X GET "http://elasticsearch:9200/products/_search?pretty"

        "_index" : "products",
        "_id" : "4",
        "_score" : 1.0,
        "_source" : {
          "title" : "Нові стоїки 52 тижні для наповненого життя",
          "description" : "Книга ''Нові стоїки. 52 тижні для наповненого життя''. Прочитана 1 раз, стан нової. Можна забрати у Львові на Зеленій.\r\n\r\nКнижка про філософію стоїцизму — вчення, яке сьогодні набуває все більше популярності. Античні стоїки уміли те..."

And i want to find by word 'Книга' (that is first in desc) but elastic searchs only in title where this word is missing and i get

{
    "count": 0,
    "next": null,
    "previous": null,
    "results": []
}

For instance i search by Нові (that is first in title) and

 "count": 1,
    "next": null,
    "previous": null,
    "results": [
        {
            "id": 4,
            "slug": "novi-stoiki-52-tizhni-dlia-napovnenogo-zhittia4",
            "title": "Нові стоїки 52 тижні для наповненого життя",
            "status": "Used",
...

Even if i add description field to serializer nothing changes

The issue likely lies in how you're interpreting the Search response in DRF, and possibly how fields are indexed or queried in Elasticsearch.

search.execute() returns an Elasticsearch DSL Response object, not a Django QuerySet, so DRF’s ListAPIView doesn't know how to serialize it.

Try Converting response to a list of actual Django objects (Product instances):

from django.db.models import Q

class SearchProductView(ListAPIView):
    serializer_class = ProductSerializer

    def get_queryset(self):
        q = self.request.GET.get("query", "")
        if q:
            search = (
                ProductDocument.search()
                .query('multi-match', query=q, fields=["title^2", "description"])
            )
            response = search.execute()
            # Get list of matching IDs
            ids = [hit.meta.id for hit in response]
            return Product.objects.filter(id__in=ids)
        return Product.objects.none()

Also make sure you actually updated the index after adding the description field by running:

python manage.py search_index --rebuild
Вернуться на верх