Фильтрация по нескольким полям в Elasticsearch ObjectField()

У меня возникают проблемы с определением синтаксиса фильтрации для ObjectFields() в django-elasticsearch-dsl. В частности, когда я пытаюсь фильтровать по нескольким подполям одного и того же ObjectField(), я получаю неправильные результаты.

Например, рассмотрим следующий документ

class ItemDocument(Document):
    product = fields.ObjectField(properties={
        'id': fields.IntegerField(),
        'name': fields.TextField(),
        'description': fields.TextField()
    })
    details = fields.ObjectField(properties={
        'category_id': fields.IntegerField(),
        'id': fields.IntegerField(),
        'value': fields.FloatField()
    })
    description = fields.TextField()

Я хочу найти элемент с детальным объектом, который имеет category_id == 3 и value < 1.5, поэтому я создал следующий запрос

x = ItemDocument.search().filter(Q("match",details__category_id=3) & Q("range",details__value={'lt':1.5})).execute()

К сожалению, это возвращает все элементы, которые имеют объект детализации с category_id==3 и отдельный объект детализации с value < 1.5, например,

{
  "product": ...
  "details": [
    {
      "category_id": 3,
      "id": 7,
      "value": 20.0
    },
    {
      "category_id": 4,
      "id": 7,
      "value": 1.0
    },
    ...
]
}

вместо желаемого результата всех элементов, которые имеют объект детализации с category_id==3 и value < 1.5, например,

{
  "product": ...
  "details": [
    {
      "category_id": 3,
      "id": 7,
      "value": 1.0
    },
    ...
]
}

Как правильно оформить этот запрос с помощью django-elasticsearch-dsl?

Вы можете использовать вложенный запрос в Elasticsearch для фильтрации по нескольким подполям одного и того же поля ObjectField. Вот пример того, как это можно сделать в django-elasticsearch-dsl:

x = ItemDocument.search().query(
    "nested",
    path="details",
    query=Q("match", details__category_id=3) & Q("range", details__value={'lt':1.5})
).execute()

Вложенный запрос позволяет фильтровать по нескольким подполям объекта details и возвращать только документы, в которых есть объект detail с category_id==3 и значением < 1.5.

Вы также можете использовать опцию inner_hits с вложенным запросом, чтобы получить подробную информацию о совпадающих детальных объектах:

x = ItemDocument.search().query(
    "nested",
    path="details",
    query=Q("match", details__category_id=3) & Q("range", details__value={'lt':1.5}),
    inner_hits={}
).execute()

Это добавит вложенное поле к каждому результату поиска, которое содержит сведения о совпадающих объектах деталей. Вы можете получить доступ к этому полю в своем коде следующим образом:

results = x.to_dict()
for result in results['hits']['hits']:
    nested_results = result['inner_hits']['details']['hits']['hits']
    # do something with nested_results
Вернуться на верх