I am having trouble figuring out the filtering syntax for ObjectFields() in django-elasticsearch-dsl. In particular, when I try to filter on multiple subfields of the same ObjectField(), I'm getting incorrect results.
For example, consider the following document
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()
I want to find an Item with a detail object that has both category_id == 3
and value < 1.5
, so I created the following query
x = ItemDocument.search().filter(Q("match",details__category_id=3) & Q("range",details__value={'lt':1.5})).execute()
Unfortunately, this returns all items which have a detail object with category_id==3
and a separate detail object with value < 1.5
e.g.
{
"product": ...
"details": [
{
"category_id": 3,
"id": 7,
"value": 20.0
},
{
"category_id": 4,
"id": 7,
"value": 1.0
},
...
]
}
instead of my desired result of all items that have a detail object with both category_id==3
AND value < 1.5
e.g.
{
"product": ...
"details": [
{
"category_id": 3,
"id": 7,
"value": 1.0
},
...
]
}
How do I properly format this query using django-elasticsearch-dsl?
CodePudding user response:
You can use the nested query in Elasticsearch to filter on multiple subfields of the same ObjectField. Here is an example of how you can do this in 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()
The nested query allows you to filter on multiple subfields of the details object, and only return documents that have a detail object with both category_id==3 and value < 1.5.
You can also use the inner_hits option with the nested query to get the details of the matching detail objects:
x = ItemDocument.search().query(
"nested",
path="details",
query=Q("match", details__category_id=3) & Q("range", details__value={'lt':1.5}),
inner_hits={}
).execute()
This will add a nested field to each search result, which contains the details of the matching detail objects. You can access this field in your code like this:
results = x.to_dict()
for result in results['hits']['hits']:
nested_results = result['inner_hits']['details']['hits']['hits']
# do something with nested_results