Files
ba16b2c189 docs: vector and analytics storage (#39716)
* Docs skeleton

* Create strcuture

* Revert "Docs skeleton"

This reverts commit 1788972dd2.

* Add analytics

* basic updates

* Fix docs reference

* Apply suggestions from code review

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* docs(storage): analytics buckets

* Apply suggestions from code review

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* Apply suggestions from code review

* format

---------

Co-authored-by: Danny White <3104761+dnywh@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: fenos <fabri.feno@gmail.com>
Co-authored-by: Charis <26616127+charislam@users.noreply.github.com>
2025-11-24 20:57:52 +00:00

224 lines
5.7 KiB
Plaintext

---
title: 'PyIceberg'
---
<Admonition type="caution" title="This feature is in alpha">
Expect rapid changes, limited features, and possible breaking updates. [Share feedback](https://github.com/orgs/supabase/discussions/40116) as we refine the experience and expand access.
</Admonition>
PyIceberg is a Python client for Apache Iceberg that enables programmatic interaction with Iceberg tables. Use it to create, read, update, and delete data in your analytics buckets.
## Installation
```bash
pip install pyiceberg pyarrow
```
## Basic setup
Here's a complete example showing how to connect to your Supabase analytics bucket and perform operations:
```python
from pyiceberg.catalog import load_catalog
import pyarrow as pa
import datetime
# Configuration - Update with your Supabase credentials
PROJECT_REF = "your-project-ref"
WAREHOUSE = "your-analytics-bucket-name"
SERVICE_KEY = "your-service-key"
# S3 credentials from Project Settings > Storage
S3_ACCESS_KEY = "your-access-key"
S3_SECRET_KEY = "your-secret-key"
S3_REGION = "us-east-1"
# Construct Supabase endpoints
S3_ENDPOINT = f"https://{PROJECT_REF}.supabase.co/storage/v1/s3"
CATALOG_URI = f"https://{PROJECT_REF}.supabase.co/storage/v1/iceberg"
# Load the Iceberg REST Catalog
catalog = load_catalog(
"supabase-analytics",
type="rest",
warehouse=WAREHOUSE,
uri=CATALOG_URI,
token=SERVICE_KEY,
**{
"py-io-impl": "pyiceberg.io.pyarrow.PyArrowFileIO",
"s3.endpoint": S3_ENDPOINT,
"s3.access-key-id": S3_ACCESS_KEY,
"s3.secret-access-key": S3_SECRET_KEY,
"s3.region": S3_REGION,
"s3.force-virtual-addressing": False,
},
)
print("✓ Successfully connected to Iceberg catalog")
```
## Creating tables
```python
# Create a namespace for organization
catalog.create_namespace_if_not_exists("analytics")
# Define the schema for your Iceberg table
schema = pa.schema([
pa.field("event_id", pa.int64()),
pa.field("user_id", pa.int64()),
pa.field("event_name", pa.string()),
pa.field("event_timestamp", pa.timestamp("ms")),
pa.field("properties", pa.string()),
])
# Create the table
table = catalog.create_table_if_not_exists(
("analytics", "events"),
schema=schema
)
print("✓ Created table: analytics.events")
```
## Writing data
```python
import datetime
# Prepare your data
current_time = datetime.datetime.now()
data = pa.table({
"event_id": [1, 2, 3, 4, 5],
"user_id": [101, 102, 101, 103, 102],
"event_name": ["login", "view_product", "logout", "purchase", "login"],
"event_timestamp": [current_time] * 5,
"properties": [
'{"browser":"chrome"}',
'{"product_id":"123"}',
'{}',
'{"amount":99.99}',
'{"browser":"firefox"}'
],
})
# Append data to the table
table.append(data)
print("✓ Appended 5 rows to analytics.events")
```
## Reading data
```python
# Scan the entire table
scan_result = table.scan().to_pandas()
print(f"Total rows: {len(scan_result)}")
print(scan_result.head())
# Query with filters
filtered = table.scan(
filter="event_name = 'login'"
).to_pandas()
print(f"Login events: {len(filtered)}")
# Select specific columns
selected = table.scan(
selected_fields=["user_id", "event_name", "event_timestamp"]
).to_pandas()
print(selected.head())
```
## Advanced operations
### Listing tables and namespaces
```python
# List all namespaces
namespaces = catalog.list_namespaces()
print("Namespaces:", namespaces)
# List tables in a namespace
tables = catalog.list_tables("analytics")
print("Tables in analytics:", tables)
# Get table metadata
table_metadata = catalog.load_table(("analytics", "events"))
print("Schema:", table_metadata.schema())
print("Partitions:", table_metadata.partitions())
```
### Handling errors
```python
try:
# Attempt to load a table
table = catalog.load_table(("analytics", "nonexistent"))
except Exception as e:
print(f"Error loading table: {e}")
# Check if table exists before creating
namespace = "analytics"
table_name = "events"
try:
existing_table = catalog.load_table((namespace, table_name))
print(f"Table already exists")
except Exception:
print(f"Table does not exist, creating...")
table = catalog.create_table((namespace, table_name), schema=schema)
```
## Performance tips
- **Batch writes** - Insert data in batches rather than row-by-row for better performance
- **Partition strategies** - Use partitioning for large tables to improve query performance
- **Schema evolution** - PyIceberg supports schema changes without rewriting data
- **Data format** - Use Parquet for efficient columnar storage
## Complete example: ETL pipeline
```python
from pyiceberg.catalog import load_catalog
import pyarrow as pa
import pandas as pd
# Setup (see Basic Setup section above)
catalog = load_catalog(...)
# Step 1: Create analytics namespace
catalog.create_namespace_if_not_exists("warehouse")
# Step 2: Define table schema
schema = pa.schema([
pa.field("id", pa.int64()),
pa.field("name", pa.string()),
pa.field("created_at", pa.timestamp("ms")),
])
# Step 3: Create table
table = catalog.create_table_if_not_exists(
("warehouse", "products"),
schema=schema
)
# Step 4: Load data from CSV or database
df = pd.read_csv("products.csv")
data = pa.Table.from_pandas(df)
# Step 5: Write to analytics bucket
table.append(data)
print(f"✓ Loaded {len(data)} products to warehouse.products")
# Step 6: Verify
result = table.scan().to_pandas()
print(result.describe())
```
## Next steps
- [Query with Postgres](/docs/guides/storage/analytics/query-with-postgres)
- [Analyze with Apache Spark](/docs/guides/storage/analytics/examples/apache-spark)
- [Explore with DuckDB](/docs/guides/storage/analytics/examples/duckdb)