Mock Predicate Pushdown in Python for Big Data Queries
Simulate predicate pushdown by applying filters at the storage layer before materializing rows, showing how big data engines optimize queries.
Python code
45 linesclass Query:
def __init__(self, table, rows):
self.table = table
self.rows = rows
def filter(self, predicate):
return Query(
self.table,
[row for row in self.rows if all(predicate(row) for predicate in predicate)]
)
def filter_pushdown(self, predicate, column):
"""
Mock predicate pushdown: apply predicate at the 'storage' level
before reading full rows. Simulates pushing filter into scan.
"""
# Simulate storage-level scan returning only matching rows
# (as if the predicate was applied in the storage engine)
storage_rows = [row for row in self.rows if predicate(row)]
# This is where real pushdown would avoid materializing full data
return Query(self.table, storage_rows)
def execute(self):
return self.rows
if __name__ == "__main__":
# Simulate a table with 5 rows
rows = [
{"id": 1, "name": "Alice", "age": 30},
{"id": 2, "name": "Bob", "age": 25},
{"id": 3, "name": "Charlie", "age": 35},
{"id": 4, "name": "Diana", "age": 28},
{"id": 5, "name": "Eve", "age": 32},
]
q = Query("users", rows)
# Push predicate into "storage" (simulated)
pushed = q.filter_pushdown(lambda r: r["age"] > 30, "age")
print("Rows after predicate pushdown (filter age > 30):")
for row in pushed.execute():
print(row)
Output
Rows after predicate pushdown (filter age > 30):
{'id': 3, 'name': 'Charlie', 'age': 35}
{'id': 5, 'name': 'Eve', 'age': 32}
How it works
Predicate pushdown is a core optimization in databases and big data engines where filters are applied as early as possible during the scan, reducing the amount of data read from storage. This mock demonstrates the concept by applying the predicate to the raw rows list before storing the filtered result in the Query object. The filter_pushdown method simulates what happens at the storage/scan level in engines like Spark or Presto, where the filter is pushed into the file reader or columnar scanner. This contrasts with a naive filter that applies the predicate only after rows are fully materialized, which would waste I/O on non-matching rows.
Common mistakes
- Confusing the mocked pushdown with actual Spark behavior — this is only a simulation of the concept
- Forgetting that real pushdown works on columnar formats where only needed columns are read
- Applying the predicate twice instead of tracking whether pushdown already happened
Variations
- Wrap the mock in a function that accepts both a column name and predicate for a more realistic filter_pushdown(column, predicate) signature
- Add a `limit` method that also gets pushed down to storage, simulating projection + filter pushdown together
Real-world use cases
- Optimizing Spark DataFrame queries where filters are pushed into Parquet file scanners to skip irrelevant row groups.
- Reducing I/O in Presto/Trino queries on partitioned Hive tables by pushing partition predicates to the metastore.
- Building query planner tools that demonstrate cost savings before implementing pushdown in a custom data engine.
Sponsored
More from Big data & Spark
Keep learning
Related tutorials and quizzes for this topic.