Evaluate a Nested Predicate Expression Tree
MongoDBSoftware Engineer · Technical ScreenLast updated
Implement an evaluator for a serialized predicate expression tree against JSON documents, testing recursion, AST handling, and field resolution.
Problem Description
Database query engines often represent filter conditions as an Abstract Syntax Tree (AST) of predicates before executing them against candidate documents.
Implement a function evaluate(predicate: dict, document: dict) -> bool that takes a serialized predicate tree and a document, returning True if the document matches the predicate and False otherwise.
Predicate Grammar
A predicate node is represented as a dictionary and can be one of two types:
-
Logical Operator Node:
{"$and": [predicate_1, predicate_2, ...]}: Evaluates toTrueif all sub-predicates evaluate toTrue. An empty list evaluates toTrue.{"$or": [predicate_1, predicate_2, ...]}: Evaluates toTrueif at least one sub-predicate evaluates toTrue. An empty list evaluates toFalse.{"$not": predicate}: Evaluates toTrueif the child predicate evaluates toFalse, and vice versa.
-
Comparison (Leaf) Node:
{"field": str, "op": str, "val": any}field: A string specifying the field name. Nested fields are separated by dot notation (e.g.,"user.address.zip").op: One of the following comparison operators:"$eq": Document value equalsval."$ne": Document value does not equalval."$gt": Document value is strictly greater thanval."$gte": Document value is greater than or equal toval."$lt": Document value is strictly less thanval."$lte": Document value is less than or equal toval."$in": Document value is contained inval(wherevalis a list).
If a field path does not exist in the document, any comparison other than "$ne" evaluates to False. For "$ne", a missing field evaluates to True.
Function Signature
def evaluate(predicate: dict, document: dict) -> bool:
pass
Examples
Example 1
predicate = {
"$and": [
{"field": "age", "op": "$gte", "val": 18},
{"field": "status", "op": "$eq", "val": "active"}
]
}
document = {"name": "Alice", "age": 22, "status": "active"}
# Output: True
Example 2
predicate = {
"$or": [
{"field": "user.role", "op": "$in", "val": ["admin", "moderator"]},
{
"$not": {
"field": "user.banned", "op": "$eq", "val": True
}
}
]
}
document = {"user": {"role": "guest", "banned": False}}
# Output: True (user.role fails $in, but not user.banned is True)
Constraints
- The maximum depth of the predicate tree is
100. - Field paths only contain alphanumeric characters and dots (
.). - Leaf comparisons (
$gt,$gte,$lt,$lte) will only be evaluated against values of compatible, comparable types (e.g., numbers vs. numbers, strings vs. strings). - Field names and operator keys are well-formed according to the specification above.
Hints
Hint 1
Separate your solution into two distinct parts: resolving a dotted field path from the document, and recursively evaluating predicate nodes.
Hint 2
Pay special attention to the base-case identity elements: an empty `$and` evaluates to `True`, while an empty `$or` evaluates to `False`.
Hint 3
When traversing nested dot paths (e.g., 'a.b.c'), ensure you safely handle cases where an intermediate key does not exist or points to a non-dictionary value.
Follow-up questions
- How would you reorder children inside `$and` and `$or` nodes to optimize short-circuiting if you had historical cost or selectivity estimates?
- How would you extend this system to support filtering elements inside arrays (e.g., MongoDB's `$elemMatch`)?
- Instead of interpreting the tree recursively on every document, how could you compile the AST into a single Python lambda or bytecode for high-throughput batch evaluation?
Solution walkthrough is for members
Get the full walkthrough with reference code and complexity analysis, plus Kitty Coach at 300 messages a day. Hints, the code runner and community answers stay free.