Search code examples
pythonpython-3.xelasticsearchelasticsearch-dsl-py

Elasticsearch-dsl nested filtering


I'm using elasticsearch-dsl library and my Documents are like these:

class Market(InnerDoc):
    market_no = Integer()

class Event(Document):
    event_id = Integer()
    markets = Nested(Market)

I want to get all Events and within these Events I want to filter some markets. How can I achive this by using elasticsearch-dsl?

Example data:

events = [
    {'event_id': 1, 'markets': [{'market_no': 1}, {'market_no': 2}, {'market_no': 3}]}, 
    {'event_id': 2, 'markets': [{'market_no': 1}]}, 
    {'event_id': 3, 'markets': [{'market_no': 1}, {'market_no': 11}]}
]

I want all events and within these, filtering markets that market_no lower or equal to 2.

results = [
    {'event_id': 1, 'markets': [{'market_no': 1}, {'market_no': 2}]}, 
    {'event_id': 2, 'markets': [{'market_no': 1}]}, 
    {'event_id': 3, 'markets': [{'market_no': 1}]}
]

Thanks.


Solution

  • In order to get events which has at least one market with market_no <= 2:

    Event.search().query(
        Nested(path='markets', query=Range(markets__market_no={'lte':2}))
    )
    

    If you want to get all events despite of markets.market_no values (if no markets with market_no <=2 => results in empty markets list for event)

    It'll be more complicated but still possible (only query in this example):

    from elasticsearch_dsl.query import Nested, Range, Bool, MatchAll
    
    Bool(should=[MatchAll(), Nested(path='markets', query=Range(markets__market_no={'lte':2}), inner_hits={"size": 100})])
    

    You'll have to set inner hits limit by yourself and extract market matches from separate field in the response (it won't be in the _source but under hits.hits.inner_hits.markets)