Skip to main content

Lazy Scan

SigilYX integrates with Polars' lazy evaluation engine. A lazy scan reads only the YXDB header upfront - the actual data is streamed from Rust when you call .collect().

Basic Usage

import polars as pl
import sigilyx

lf = pl.scan_yxdb("data.yxdb")
print(type(lf)) # <class 'polars.LazyFrame'>

# Build a query plan
query = (
lf
.filter(pl.col("amount") > 100)
.select("id", "name", "amount")
.sort("amount", descending=True)
.head(50)
)

# Data is read and processed only now
result = query.collect()

The equivalent direct API:

import sigilyx as yx

lf = yx.scan("data.yxdb")

Pushdown Support

Polars pushes optimizations down into the SigilYX reader where possible:

OptimizationSupportedEffect
Projection pushdownYesOnly selected columns are decoded
Row-limit pushdownYesReading stops after n_rows / .head()
Predicate pushdownNoFilters applied post-scan

Why no predicate pushdown?

YXDB rows are packed into LZF-compressed blocks with no block-level statistics (min/max, bloom filters, etc.). The reader must decompress and decode every block to evaluate filters. This is a format limitation, not a SigilYX limitation.

tip

For filter-heavy queries on large files, consider converting to Parquet first. Parquet supports predicate pushdown via row-group statistics, which can skip large portions of the file entirely.

Projection Pushdown in Action

import polars as pl
import sigilyx

# Only "id" and "name" are decoded from the YXDB file
result = pl.scan_yxdb("wide_file.yxdb").select("id", "name").collect()

For a 50-column file where you only need 2 columns, this is dramatically faster and uses much less memory than reading all columns.

Row-Limit Pushdown

# Reading stops after 100 rows - the rest of the file is never touched
preview = pl.scan_yxdb("big_file.yxdb").head(100).collect()

Combining with Other Lazy Sources

Polars LazyFrames compose naturally:

import polars as pl
import sigilyx

yxdb_data = pl.scan_yxdb("legacy.yxdb")
parquet_data = pl.scan_parquet("modern.parquet")

# Join across formats - everything stays lazy until .collect()
result = (
yxdb_data
.join(parquet_data, on="customer_id", how="inner")
.group_by("region")
.agg(pl.col("revenue").sum())
.collect()
)

When to Use Lazy vs Eager

ScenarioRecommendation
Small files (< 100 MB)Eager pl.read_yxdb() is simpler
Large files, need all columnsEager or streaming batches
Large files, need few columnsLazy pl.scan_yxdb() - projection pushdown shines
Preview / samplingLazy with .head() - row-limit pushdown
Complex multi-source pipelinesLazy - let Polars optimize the plan