Search code examples
pythonelasticsearchelasticsearch-dslelasticsearch-dsl-pyq-query

elasticsearch dsl python unpack q queries


How can I dynamically create a combined Q query in elasticsearch dsl python? I have gone through docs and this SO post. I constructed a q_query dictionary with all required info.

ipdb> q_queries
{'queries': [{'status': 'PASS'}, {'status': 'FAIL'}, {'status': 'ABSE'}, {'status': 'WITH'}], 'operation': 'or'}

I want to perform the following q_query

qq=Q("match", status='PASS') | Q("match", status="FAIL") | Q("match", status="ABSE") | Q("match", status="WITH")

for a list of dict following works out

ipdb> [Q('match', **z) for z in q_queries['queries']]
[Match(status='PASS'), Match(status='FAIL'), Match(status='ABSE'), Match(status='WITH')]

But How to combine multiple Qs with an or operator or an and operator? Also what is the corresponding elasticsearch raw query for the above? I tried following since I have to filter based on test_id.

{
  "query": {
    "bool": {
      "must":     [
        { "match": { "test_id": "7" }},
        {
          "range": {
            "created": {
              "gte": "2016-01-01",
              "lte": "2016-01-31"
            }
          }
        }
        ],
      "should": [
                  { "match": { "status": "PASS"}},
                  { "match": { "status": "FAIL"}}
      ]
    }
  }
}

But results are not as expected I same query without should filter and the results obtained were the same. So should filters were not executed by elasticsearch in my case. Any help is much appreciated.

TIA


Solution

  • After exploring elasticsearch dsl python for some more time, this piece of documentation helped me to solve above issue. Below posted is the function that I wrote to resolve this issue.

    def create_q_queries(self, q_queries, search_query):
        """
        create q queries and chain if multiple q queries.
        :param q_queries: Q queries with operation and query params as a dict.
        :param search_query: Search() object.
        :return: search_query updated with q queries.
        """
        if q_queries:
            logical_operator_mappings = {'or': 'should', 'and': 'must'}
            for query in q_queries:
                queries = [Q('match', **query) for query in query['queries']]
                search_query = search_query.query(Q('bool', **{
                    logical_operator_mappings.get(query.get('operation')): queries
                }))
        return search_query  
    

    I changed format of q_queries to perform chaining based on multiple operators like and, or etc.

    q_queries = [
                  {
                    "operation": "or",
                    "queries":
                      [
                        {"status": "PASS"}, {"status": "FAIL"}, {"status": "ABSE"}, {"status": "WITH"}
                      ]
                    }
    
                 ]