How to build a dynamic query with mongoengine

An example of the use of the Q class to build the query.

An input from a frontend:

query_input = [
    [
        {
            "field": "some_field",
            "operator": "gt",
            "value": 30
        },
        {
            "field": "some_field",
            "operator": "lt",
            "value": 40
        },
        {
            "field": "some_field",
            "operator": "",
            "value": 35
        }
    ],
    [
        {
            "field": "another_field",
            "operator": "istartswith",
            "value": "test"
        }
    ]   
]

Assume that the first-level arrays should be join by AND and the dicts in the second-level arrays should be joined by OR. The solution may look like:

import json
from functools import reduce
from mongoengine import Q

def prepare_condition(condition):
    field = [condition['field'], condition['operator']]
    field = (s for s in field if s)
    field = '__'.join(field)
    return {field: condition['value']}

def prepare_conditions(row):
    return (Q(**prepare_condition(condition)) for condition in row)

def join_conditions(row):
    return reduce(lambda a, b: a | b, prepare_conditions(row))

def join_rows(rows):
    return reduce(lambda a, b: a & b, rows)

query = join_rows(join_conditions(row) for row in query_input)

print(json.dumps(query.to_query(None), indent=4))

The output will be:

{
    "$and": [
        {
            "$or": [
                {
                    "some_field": {
                        "$gt": 30
                    }
                },
                {
                    "some_field": {
                        "$lt": 40
                    }
                },
                {
                    "some_field": 35
                }
            ]
        },
        {
            "another_field": "test"
        }
    ]
}
comments powered by Disqus