| Crates.io | polars-redis |
| lib.rs | polars-redis |
| version | 0.1.6 |
| created_at | 2026-01-03 22:55:34.571284+00 |
| updated_at | 2026-01-06 22:22:16.179881+00 |
| description | Redis IO plugin for Polars |
| homepage | |
| repository | https://github.com/joshrotenberg/polars-redis |
| max_upload_size | |
| id | 2020902 |
| size | 1,761,593 |
Query Redis like a database. Transform with Polars. Write back without ETL.
New to Polars? Polars is a lightning-fast DataFrame library for Python and Rust. Check out the Polars User Guide to get started.
polars-redis makes Redis a first-class data source in your Polars workflows. Query, transform, and write back - Redis becomes just another connector alongside Parquet, CSV, and databases.
import polars as pl
import polars_redis as redis
# Redis is just another source
users = redis.read_hashes(url, "user:*", schema)
orders = pl.read_parquet("s3://bucket/orders.parquet")
# Full Polars transformation power
result = (
users.join(orders, on="user_id")
.group_by("region")
.agg(pl.col("amount").sum())
)
# Write back to Redis
redis.write_hashes(result, url, key_prefix="region_stats:")
pip install polars-redis
| Your data | Use | Why |
|---|---|---|
| User profiles, configs | scan_hashes() |
Field-level access, projection pushdown |
| Nested documents | scan_json() |
Full document structure |
| Counters, flags, caches | scan_strings() |
Simple key-value |
| Tags, memberships | scan_sets() |
Unique members |
| Queues, recent items | scan_lists() |
Ordered elements |
| Leaderboards, rankings | scan_zsets() |
Score-based ordering |
import polars as pl
import polars_redis as redis
url = "redis://localhost:6379"
# Scan with schema
lf = redis.scan_hashes(
url,
pattern="user:*",
schema={"name": pl.Utf8, "age": pl.Int64, "active": pl.Boolean},
)
# Filter and collect (projection pushdown applies)
active_users = lf.filter(pl.col("active")).select(["name", "age"]).collect()
# Write with TTL
redis.write_hashes(active_users, url, key_prefix="cache:user:", ttl=3600)
from polars_redis import col
# Filter in Redis, not Python - only matching docs are transferred
df = redis.search_hashes(
url,
index="users_idx",
query=(col("age") > 30) & (col("status") == "active"),
schema={"name": pl.Utf8, "age": pl.Int64, "status": pl.Utf8},
).collect()
# Server-side aggregation
df = redis.aggregate_hashes(
url,
index="users_idx",
query="*",
group_by=["@department"],
reduce=[("COUNT", [], "count"), ("AVG", ["@salary"], "avg_salary")],
)
# Speed up large scans with parallel workers
lf = redis.scan_hashes(
url,
pattern="user:*",
schema={"name": pl.Utf8, "age": pl.Int64},
parallel=4, # Use 4 parallel workers
)
Read:
scan_hashes() / read_hashes() - Redis hashesscan_json() / read_json() - RedisJSON documentsscan_strings() / read_strings() - Redis stringsscan_sets() / read_sets() - Redis setsscan_lists() / read_lists() - Redis listsscan_zsets() / read_zsets() - Redis sorted setsinfer_hash_schema(), infer_json_schema())RediSearch (Predicate Pushdown):
search_hashes() - Server-side filtering with FT.SEARCHaggregate_hashes() - Server-side aggregation with FT.AGGREGATEcol("age") > 30Write:
write_hashes(), write_json(), write_strings()write_sets(), write_lists(), write_zsets()| Polars | Redis |
|---|---|
Utf8 |
string |
Int64 |
parsed int |
Float64 |
parsed float |
Boolean |
true/false, 1/0, yes/no |
Date |
YYYY-MM-DD or epoch days |
Datetime |
ISO 8601 or Unix timestamp |
Full documentation at joshrotenberg.github.io/polars-redis
MIT or Apache-2.0