Skip to main content

A modern, type-safe ORM for ClickHouse with full support for ClickHouse-specific features

Project description

CHORM - ClickHouse ORM

A powerful SQLAlchemy-like ORM for ClickHouse, optimized for analytics and high-performance queries.

Features

Core ORM Capabilities

  • SQLAlchemy-like Syntax: Familiar API for Python developers
  • Native ClickHouse Types: Full support for ClickHouse-specific types (UInt64, Array, Nullable, etc.)
  • Type Safety: Explicit type declarations for better IDE support
  • Async Support: Full async/await support with AsyncSession
  • Query Builder: Fluent API for SELECT, INSERT, UPDATE, DELETE

Analytics & Advanced Queries

  • Window Functions: ROW_NUMBER, RANK, DENSE_RANK, LAG, LEAD, and aggregate window functions
  • CTEs (Common Table Expressions): Build complex queries with readable structure
  • JOINs: INNER, LEFT, RIGHT, FULL, CROSS with multiple join support
  • Subqueries: Scalar and table subqueries with IN/EXISTS support
  • Conditional Aggregations: sumIf, countIf, avgIf for multi-metric queries

ClickHouse-Specific Features

  • ARRAY JOIN: Efficiently unnest and analyze array columns
  • LIMIT BY: Top-N rows per group without window functions
  • WITH TOTALS: Add summary rows to GROUP BY results
  • PREWHERE: Optimize queries with early filtering
  • FINAL, SAMPLE, SETTINGS: Full control over query execution
  • OPTIMIZE TABLE: Manual table optimization and deduplication
  • INSERT FROM SELECT: Efficient bulk data copying
  • Dictionaries: External data source integration
  • EXPLAIN/ANALYZE: Query profiling and optimization

Installation

pip install clickhouse-chorm

Quick Start

Define Models

from chorm import Table, Column
from chorm.types import UInt64, String
from chorm.table_engines import MergeTree

class User(Table):
    __tablename__ = "users"
    
    id = Column(UInt64(), primary_key=True)
    name = Column(String())
    email = Column(String())
    
    engine = MergeTree()

Synchronous Usage

from chorm import create_engine, Session, select

# Create engine with timeout configuration
engine = create_engine(
    "clickhouse://localhost:8123/default",
    connect_timeout=5,           # Connection timeout in seconds
    send_receive_timeout=120,    # Query timeout in seconds
    compress='lz4'               # Enable compression
)

# Create session
session = Session(engine)

# Insert data
user = User(id=1, name="Alice", email="alice@example.com")
session.add(user)
session.commit()

# Query data
stmt = select(User.id, User.name).where(User.id > 0)
result = session.execute(stmt)
users = result.all()

Asynchronous Usage

from chorm import create_async_engine, AsyncSession

# Create async engine
engine = create_async_engine("clickhouse://localhost:8123/default")

# Use async session
async with AsyncSession(engine) as session:
    user = User(id=1, name="Alice", email="alice@example.com")
    session.add(user)
    # Auto-commits on exit

# Query asynchronously
async with AsyncSession(engine) as session:
    stmt = select(User).where(User.id > 0)
    result = await session.execute(stmt)
    users = result.all()

Production Features

Connection Pooling

CHORM supports connection pooling for improved performance in high-concurrency applications:

from chorm import create_engine

# Enable connection pooling
engine = create_engine(
    "clickhouse://localhost:8123/default",
    pool_size=10,        # Maximum pooled connections
    max_overflow=20,     # Additional overflow connections
    pool_timeout=30.0,   # Connection acquisition timeout
    pool_recycle=3600    # Recycle connections after 1 hour
)

# Connections are automatically managed
with engine.connection() as conn:
    result = conn.query("SELECT * FROM users")
# Connection automatically returned to pool

Async Connection Pooling:

from chorm import create_async_engine

# Enable async connection pooling
engine = create_async_engine(
    "clickhouse://localhost:8123/default",
    pool_size=10,
    max_overflow=20
)

# Use async context manager
async with engine.connection() as conn:
    result = await conn.query("SELECT * FROM users")
# Connection automatically returned to pool

Benefits:

  • 5-10x query throughput improvement for high-frequency queries
  • Reduced connection overhead
  • Thread-safe (sync) and asyncio-safe (async)
  • Automatic connection recycling

See Connection Pooling Guide for detailed examples.

Retry Logic with Exponential Backoff

Automatically retry failed operations with configurable backoff:

from chorm import with_retry, RetryConfig

# Configure retry behavior
retry_config = RetryConfig(
    max_attempts=3,       # Maximum retry attempts
    initial_delay=0.1,    # Initial delay in seconds
    max_delay=10.0,       # Maximum delay cap
    exponential_base=2.0, # Backoff multiplier
    jitter=True           # Add random jitter
)

# Apply to any function
@with_retry(retry_config)
def fetch_critical_data():
    with engine.connection() as conn:
        return conn.query("SELECT * FROM critical_table")

# Automatically retries on transient errors
result = fetch_critical_data()

Async Retry:

from chorm import async_with_retry

@async_with_retry(RetryConfig(max_attempts=5))
async def fetch_data():
    async with engine.connection() as conn:
        return await conn.query("SELECT * FROM users")

result = await fetch_data()

Features:

  • Exponential backoff with jitter to avoid thundering herd
  • Configurable retryable error types
  • Works with connection pooling
  • Automatic retry on network errors, timeouts, and memory errors

Health Checks

Monitor ClickHouse connection health:

from chorm import create_engine, HealthCheck

engine = create_engine("clickhouse://localhost:8123/default")
health = HealthCheck(engine)

# Quick ping check
if health.ping():
    print("ClickHouse is reachable")

# Detailed status
status = health.get_status()
print(f"Status: {status['status']}")           # "healthy" or "unhealthy"
print(f"Latency: {status['latency_ms']}ms")    # Response time
print(f"Version: {status['version']}")          # ClickHouse version
print(f"Uptime: {status['uptime_seconds']}s")  # Server uptime

# Server information
info = health.get_server_info()
print(f"Database: {info['current_database']}")
print(f"Memory: {info['used_memory']} / {info['total_memory']}")

Async Health Checks:

from chorm import create_async_engine, AsyncHealthCheck

engine = create_async_engine("clickhouse://localhost:8123/default")
health = AsyncHealthCheck(engine)

# Async health check
if await health.ping():
    status = await health.get_status()
    print(f"Latency: {status['latency_ms']}ms")

Use Cases:

  • Kubernetes liveness/readiness probes
  • Load balancer health checks
  • Monitoring and alerting
  • Circuit breaker implementations

Query Execution Metrics

Track and analyze query performance:

from chorm import MetricsCollector, enable_global_metrics

# Create metrics collector
collector = MetricsCollector(
    slow_query_threshold_ms=500.0,  # Queries >500ms are "slow"
    log_all_queries=False            # Only log slow queries
)

# Measure query execution
with collector.measure("SELECT * FROM users WHERE active = 1") as metrics:
    result = session.execute(query)

# Get summary statistics
summary = collector.get_summary()
print(f"Total queries: {summary['total_queries']}")
print(f"Average duration: {summary['avg_duration_ms']:.2f}ms")
print(f"Slow queries: {summary['slow_queries']}")

# Get percentiles
percentiles = collector.get_percentiles([50, 90, 95, 99])
print(f"P95: {percentiles['p95']:.2f}ms")

# Get slow queries
slow_queries = collector.get_slow_queries()
for metric in slow_queries:
    print(f"Slow: {metric.sql[:50]} - {metric.duration_ms:.2f}ms")

Global Metrics:

# Enable global metrics collection
collector = enable_global_metrics(slow_query_threshold_ms=500.0)

# All queries are now tracked automatically
with collector.measure("SELECT 1"):
    result = session.execute(query)

# Get summary
summary = collector.get_summary()

Connection Pool Statistics

Monitor pool utilization and performance:

# Get pool statistics
stats = engine.pool.get_statistics()

print(f"Pool size: {stats['pool_size']}")
print(f"Connections in use: {stats['connections_in_use']}")
print(f"Overflow: {stats['overflow']}")
print(f"Utilization: {stats['utilization_percent']}%")

Complete Monitoring Example:

from chorm import (
    create_engine, MetricsCollector, HealthCheck
)

# Setup with pooling
engine = create_engine(
    "clickhouse://localhost:8123/default",
    pool_size=10,
    max_overflow=20
)

# Setup monitoring
collector = MetricsCollector(slow_query_threshold_ms=500.0)
health = HealthCheck(engine)

# Check health
if health.ping():
    status = health.get_status()
    print(f"✓ Healthy - Latency: {status['latency_ms']}ms")
else:
    print("✗ Unhealthy - Cannot reach ClickHouse")

# Execute queries with metrics
with collector.measure("SELECT * FROM users"):
    with engine.connection() as conn:
        result = conn.query("SELECT * FROM users LIMIT 100")

# Monitor pool
pool_stats = engine.pool.get_statistics()
print(f"Pool utilization: {pool_stats['utilization_percent']}%")

# Check for slow queries
if collector.get_slow_queries():
    print("⚠ Slow queries detected!")

See examples/monitoring_demo.py for complete examples.

ClickHouse-Optimized Batch Insert

Efficiently insert large volumes of data with ClickHouse-optimized batching:

from chorm import create_engine, bulk_insert

engine = create_engine("clickhouse://localhost:8123/default")
client = engine._client

# Prepare data (1M rows)
data = [
    [i, f"User{i}", f"user{i}@example.com"]
    for i in range(1_000_000)
]

# Insert with optimal batch size (100k rows)
stats = bulk_insert(
    client, "users", data,
    columns=["id", "name", "email"],
    batch_size=100_000  # ClickHouse-optimized
)

print(f"Inserted {stats['total_rows']:,} rows in {stats['batches_sent']} batches")

Advanced Batch Insert:

from chorm import ClickHouseBatchInsert

# Create batch inserter
batch = ClickHouseBatchInsert(
    client,
    "users",
    columns=["id", "name", "email"],
    batch_size=100_000
)

# Add rows (auto-flushes at 100k rows)
for i in range(1_000_000):
    batch.add_row([i, f"User{i}", f"user{i}@example.com"])

# Flush remaining rows
stats = batch.finish()

DataFrame Support:

import pandas as pd
from chorm import bulk_insert

df = pd.DataFrame({
    'id': range(1_000_000),
    'name': [f'User{i}' for i in range(1_000_000)]
})

# Automatically detected as DataFrame
stats = bulk_insert(client, "users", df, batch_size=100_000)

Performance Features:

  • Uses native client.insert() (5-10x faster than SQL VALUES)
  • Default batch size: 100,000 rows (ClickHouse-optimized)
  • Automatic batching and flushing
  • DataFrame support built-in
  • Optional OPTIMIZE TABLE after insertion

See examples/batch_insert_demo.py for complete examples.

Advanced Features

Performance Operations

from chorm import optimize_table, insert, select

# Optimize table (manual merge)
stmt = optimize_table(User, final=True)
session.execute(stmt.to_sql())

# Deduplicate data
stmt = optimize_table(User, deduplicate=True, final=True)
session.execute(stmt.to_sql())

# INSERT FROM SELECT (bulk data copying)
source_query = select(SourceTable.id, SourceTable.name).where(SourceTable.active == 1)
stmt = insert(TargetTable).from_select(source_query, columns=["id", "name"])
session.execute(stmt.to_sql())

Advanced Aggregates

from chorm.sql.expression import top_k, group_bitmap, any_last

# Top-K most frequent values
query = select(
    User.country,
    func.count().label("count")
).select_from(User).group_by(User.country).order_by(func.count().desc()).limit(10)

# Bitmap aggregation for unique counts
query = select(
    Event.date,
    group_bitmap(Event.user_id).label("unique_users")
).select_from(Event).group_by(Event.date)

# Sampling aggregates
query = select(
    User.country,
    any_last(User.last_login).label("latest_login")
).select_from(User).group_by(User.country)

Dictionary Support

from chorm import create_dictionary
from chorm.sql.expression import dict_get, dict_has

# Create dictionary
stmt = create_dictionary(
    "user_dict",
    "ClickHouse(HOST 'localhost' PORT 9000 USER 'default' TABLE 'users' DB 'default')",
    "HASHED",
    [("id", "UInt64"), ("name", "String"), ("country", "String")],
    lifetime=300  # Cache lifetime in seconds
)
session.execute(stmt.to_sql())

# Use dictionary in queries
query = select(
    Order.id,
    dict_get("user_dict", "name", Order.user_id).label("user_name"),
    dict_get("user_dict", "country", Order.user_id).label("user_country")
).select_from(Order)

Query Observability

# Analyze query execution
query = select(User).where(User.country == 'US')

# Get query plan
explain_stmt = query.explain(explain_type="PLAN")
result = session.execute(explain_stmt.to_sql())

# Profile query (pipeline analysis)
explain_stmt = query.analyze()  # Shortcut for EXPLAIN PIPELINE
result = session.execute(explain_stmt.to_sql())

Documentation

Development

Setup

# Clone repository
git clone https://github.com/yourusername/chorm.git
cd chorm

# Create virtual environment
python -m venv .venv
source .venv/bin/activate  # On Windows: .venv\Scripts\activate

# Install dependencies
pip install -e ".[dev]"

Running Tests

Start ClickHouse with Docker Compose:

docker-compose up -d

Wait for ClickHouse to be ready (check with docker-compose ps), then run tests:

pytest

Stop ClickHouse:

docker-compose down

Running Integration Tests

Integration tests require a running ClickHouse instance:

# Start ClickHouse
docker-compose up -d

# Wait for healthcheck
docker-compose ps

# Run all tests including integration
pytest

# Run only integration tests
pytest tests/integration/

# Stop ClickHouse
docker-compose down -v  # -v removes volumes

Query Examples

SELECT with Filters

from chorm import select, func

# Simple select
stmt = select(User.id, User.name).where(User.id > 10)

# With multiple conditions
stmt = select(User).where(
    (User.id > 10) & (User.name.like("A%"))
)

# With ordering and limit
stmt = select(User).order_by(User.name).limit(10)

# With aggregation
stmt = select(func.count(User.id)).where(User.id > 0)

GROUP BY and Aggregation

from chorm import select, func

# Simple GROUP BY
stmt = select(User.city, func.count(User.id)).group_by(User.city)

# GROUP BY with HAVING
stmt = (
    select(User.city, func.count(User.id))
    .group_by(User.city)
    .having(func.count(User.id) > 10)
)

# Multiple aggregations
stmt = (
    select(
        User.city,
        func.count(User.id).label("count"),
        func.avg(User.age).label("avg_age")
    )
    .group_by(User.city)
)

INSERT

from chorm import insert

# Single insert
stmt = insert(User).values(id=1, name="Alice", email="alice@example.com")
session.execute(stmt)

# Bulk insert via session
users = [
    User(id=1, name="Alice", email="alice@example.com"),
    User(id=2, name="Bob", email="bob@example.com"),
]
for user in users:
    session.add(user)
session.commit()

UPDATE (ClickHouse ALTER TABLE)

from chorm import update

stmt = update(User).where(User.id == 1).values(name="Alice Updated")
session.execute(stmt)

DELETE (ClickHouse ALTER TABLE)

from chorm import delete

stmt = delete(User).where(User.id == 1)
session.execute(stmt)

DDL Operations

CHORM provides comprehensive DDL (Data Definition Language) operations for managing database schema:

DROP TABLE

from chorm import drop_table

# Drop table with IF EXISTS
stmt = drop_table(User)
session.execute(stmt.to_sql())

# Drop without IF EXISTS
stmt = drop_table(User, if_exists=False)
session.execute(stmt.to_sql())

TRUNCATE TABLE

from chorm import truncate_table

# Remove all data from table
stmt = truncate_table(User)
session.execute(stmt.to_sql())

ALTER TABLE - Column Operations

from chorm import add_column, drop_column, modify_column, rename_column

# Add a column
stmt = add_column(User, "age UInt8", after="name")
session.execute(stmt.to_sql())

# Add column with default value
stmt = add_column(User, "status String DEFAULT 'active'")
session.execute(stmt.to_sql())

# Drop a column
stmt = drop_column(User, "old_field")
session.execute(stmt.to_sql())

# Modify column type
stmt = modify_column(User, "age UInt16")
session.execute(stmt.to_sql())

# Rename column
stmt = rename_column(User, "old_name", "new_name")
session.execute(stmt.to_sql())

ALTER TABLE - Index Operations

from chorm import add_index, drop_index
from chorm.sql.expression import Identifier

# Add minmax index
stmt = add_index(User, "idx_email", Identifier("email"))
session.execute(stmt.to_sql())

# Add bloom filter index
stmt = add_index(
    User, 
    "idx_name", 
    Identifier("name"),
    index_type="bloom_filter",
    granularity=4
)
session.execute(stmt.to_sql())

# Drop index
stmt = drop_index(User, "idx_email")
session.execute(stmt.to_sql())

Using DDL in Migrations

DDL operations integrate seamlessly with the migration system:

from chorm.migration import Migration
from chorm.session import Session
from chorm.sql.expression import Identifier

class AddUserAgeColumn(Migration):
    id = "20231203_001"
    name = "Add age column to users"
    
    def upgrade(self, session: Session) -> None:
        # Add column using helper method
        self.add_column(session, 'users', 'age UInt8', after='name')
        
        # Add index
        self.add_index(
            session, 
            'users', 
            'idx_age', 
            Identifier('age'),
            index_type='minmax'
        )
    
    def downgrade(self, session: Session) -> None:
        # Drop index first
        self.drop_index(session, 'users', 'idx_age')
        
        # Then drop column
        self.drop_column(session, 'users', 'age')

ClickHouse-Specific Features

PREWHERE Clause

stmt = select(User).prewhere(User.id > 1000).where(User.name == "Alice")

FINAL Modifier

stmt = select(User).final()

SAMPLE Clause

stmt = select(User).sample(0.1)  # 10% sample

SETTINGS

stmt = select(User).settings(max_threads=4, max_memory_usage=10000000000)

Analytics Examples

Window Functions

from chorm.sql.expression import window, func

# Ranking products by sales within each category
w = window(
    partition_by=[Product.category],
    order_by=[func.sum(Order.amount).desc()]
)

stmt = (
    select(
        Product.category,
        Product.name,
        func.sum(Order.amount).label('total_sales'),
        func.row_number().over(w).label('rank')
    )
    .select_from(Order)
    .join(Product, on=Order.product_id == Product.id)
    .group_by(Product.category, Product.name)
)

# Running totals
w_running = window(
    partition_by=[Order.user_id],
    order_by=[Order.created_at],
    rows_between=('UNBOUNDED PRECEDING', 'CURRENT ROW')
)

stmt = select(
    Order.id,
    Order.amount,
    func.sum(Order.amount).over(w_running).label('running_total')
).select_from(Order)

CTEs (Common Table Expressions)

# Multi-step analytics with CTEs
active_users = (
    select(User.id, User.name)
    .where(User.last_login > func.now() - Literal("INTERVAL 30 DAY"))
    .cte('active_users')
)

high_value_orders = (
    select(Order.user_id, func.sum(Order.amount).label('total'))
    .group_by(Order.user_id)
    .having(func.sum(Order.amount) > 1000)
    .cte('high_value')
)

stmt = (
    select(
        Identifier('active_users.name'),
        Identifier('high_value.total')
    )
    .select_from(Identifier('active_users'))
    .join(Identifier('high_value'), 
          on=Identifier('active_users.id') == Identifier('high_value.user_id'))
    .with_cte(active_users, high_value)
)

ARRAY JOIN

# Analyze tags from array column
stmt = (
    select(
        Product.category,
        Identifier('tag'),
        func.count().label('tag_count')
    )
    .select_from(Product)
    .array_join(Product.tags, alias='tag')
    .group_by(Product.category, Identifier('tag'))
)

Conditional Aggregations

from chorm.sql.expression import func

# Multiple metrics in one query
stmt = select(
    User.city,
    func.sumIf(Order.amount, Order.status == 'completed').label('completed_sales'),
    func.sumIf(Order.amount, Order.status == 'pending').label('pending_sales'),
    func.countIf(Order.status == 'cancelled').label('cancelled_count'),
    func.avgIf(Order.amount, Order.status == 'completed').label('avg_order')
).select_from(Order).join(User, on=Order.user_id == User.id).group_by(User.city)

LIMIT BY

# Top 3 orders per user
stmt = (
    select(Order.user_id, Order.id, Order.amount)
    .select_from(Order)
    .order_by(Order.amount.desc())
    .limit_by(3, Order.user_id)
)

Table Engines

CHORM supports all major ClickHouse table engines. Choose the right engine based on your use case:

MergeTree Family

MergeTree - Default engine for most use cases:

from chorm.table_engines import MergeTree

class User(Table):
    __tablename__ = "users"
    engine = MergeTree()
    # ORDER BY is required for MergeTree

ReplacingMergeTree - For data with updates (deduplication):

from chorm.table_engines import ReplacingMergeTree

class User(Table):
    __tablename__ = "users"
    engine = ReplacingMergeTree(version_column="updated_at")
    # Keeps only the latest version based on version_column

SummingMergeTree - For pre-aggregated metrics:

from chorm.table_engines import SummingMergeTree

class Metrics(Table):
    __tablename__ = "metrics"
    engine = SummingMergeTree(columns=["value", "count"])
    # Automatically sums numeric columns during merges

AggregatingMergeTree - For pre-aggregated data with aggregate functions:

from chorm.table_engines import AggregatingMergeTree

class AggregatedStats(Table):
    __tablename__ = "aggregated_stats"
    engine = AggregatingMergeTree()
    # Stores pre-computed aggregates

CollapsingMergeTree - For data with sign-based updates:

from chorm.table_engines import CollapsingMergeTree

class Events(Table):
    __tablename__ = "events"
    engine = CollapsingMergeTree(sign_column="sign")
    # sign=1 for insert, sign=-1 for delete

VersionedCollapsingMergeTree - CollapsingMergeTree with versioning:

from chorm.table_engines import VersionedCollapsingMergeTree

class Events(Table):
    __tablename__ = "events"
    engine = VersionedCollapsingMergeTree(sign_column="sign", version_column="version")

GraphiteMergeTree - For Graphite metrics:

from chorm.table_engines import GraphiteMergeTree

class GraphiteData(Table):
    __tablename__ = "graphite"
    engine = GraphiteMergeTree(config_element="graphite_rollup")

Replicated Engines

All MergeTree engines have replicated versions for high availability:

from chorm.table_engines import (
    ReplicatedMergeTree,
    ReplicatedReplacingMergeTree,
    ReplicatedSummingMergeTree,
    ReplicatedAggregatingMergeTree,
    ReplicatedCollapsingMergeTree,
    ReplicatedVersionedCollapsingMergeTree,
    ReplicatedGraphiteMergeTree
)

class ReplicatedUser(Table):
    __tablename__ = "users"
    engine = ReplicatedMergeTree(
        zookeeper_path="/clickhouse/tables/users",
        replica_name="replica1"
    )

Log Engines

For small tables and temporary data:

from chorm.table_engines import Log, TinyLog, StripeLog

# Log - General purpose log engine
class TempData(Table):
    __tablename__ = "temp"
    engine = Log()

# TinyLog - Minimal overhead
class SmallTable(Table):
    __tablename__ = "small"
    engine = TinyLog()

# StripeLog - Better for writes
class WriteHeavy(Table):
    __tablename__ = "writes"
    engine = StripeLog()

Special Engines

Memory - In-memory storage:

from chorm.table_engines import Memory

class Cache(Table):
    __tablename__ = "cache"
    engine = Memory()

Distributed - Distributed tables across cluster:

from chorm.table_engines import Distributed

class DistributedUser(Table):
    __tablename__ = "users_distributed"
    engine = Distributed(
        cluster="my_cluster",
        database="default",
        table="users"
    )

Kafka - Kafka integration:

from chorm.table_engines import Kafka

class KafkaEvents(Table):
    __tablename__ = "kafka_events"
    engine = Kafka()

External Data Sources:

from chorm.table_engines import MySQL, PostgreSQL, ODBC, JDBC

# MySQL
class MySQLTable(Table):
    engine = MySQL(
        host="mysql.example.com",
        database="db",
        table="table",
        user="user",
        password="pass"
    )

# PostgreSQL
class PostgresTable(Table):
    engine = PostgreSQL(
        host="postgres.example.com",
        database="db",
        table="table",
        user="user",
        password="pass"
    )

Other Engines:

  • File - File-based storage
  • Null - Discards writes, returns empty reads
  • Set - Set data structure
  • Join - Join table for JOIN operations
  • View - Materialized view

See Best Practices Guide for guidance on choosing the right engine.

ClickHouse-Specific Features

Type System

CHORM supports all ClickHouse types:

from chorm.types import (
    # Integers
    UInt8, UInt16, UInt32, UInt64,
    Int8, Int16, Int32, Int64,
    
    # Floats
    Float32, Float64,
    
    # Strings
    String, FixedString,
    
    # Dates
    Date, DateTime,
    
    # Special
    UUID, Decimal, JSON,
    
    # Composite
    Array, Nullable, Map, Tuple, LowCardinality
)

class Event(Table):
    __tablename__ = "events"
    
    id = Column(UInt64(), primary_key=True)
    tags = Column(Array(String()))
    metadata = Column(Map(String(), String()))
    optional_field = Column(Nullable(String()))
    
    engine = MergeTree()

Documentation

License

MIT

Contributing

Contributions are welcome! Please read our contributing guidelines and submit pull requests.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

clickhouse_chorm-0.1.3.tar.gz (218.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

clickhouse_chorm-0.1.3-py3-none-any.whl (104.3 kB view details)

Uploaded Python 3

File details

Details for the file clickhouse_chorm-0.1.3.tar.gz.

File metadata

  • Download URL: clickhouse_chorm-0.1.3.tar.gz
  • Upload date:
  • Size: 218.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for clickhouse_chorm-0.1.3.tar.gz
Algorithm Hash digest
SHA256 40805ac52f9eee0395953853c80312f2840c85d47412a318b4fe26fd623644ca
MD5 96edfcce722496545a9bfdeb4a8578bc
BLAKE2b-256 c2f352364138cf405921ea685852dbf41666e9a1736ebc8b0e634ce9deaa601d

See more details on using hashes here.

Provenance

The following attestation bundles were made for clickhouse_chorm-0.1.3.tar.gz:

Publisher: publish.yml on ZwickVitaly/CHORM

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file clickhouse_chorm-0.1.3-py3-none-any.whl.

File metadata

File hashes

Hashes for clickhouse_chorm-0.1.3-py3-none-any.whl
Algorithm Hash digest
SHA256 ee7cfdccc685e53ab63e68a66444ad35c3f0addd8829215d91bf75795a6349a4
MD5 c29735dcf42b3786948c78fefad01ac2
BLAKE2b-256 b7f3dfb6c6abf8cf73bc5e564f45d66b4d8c1131efcde5820f7019dd49363d90

See more details on using hashes here.

Provenance

The following attestation bundles were made for clickhouse_chorm-0.1.3-py3-none-any.whl:

Publisher: publish.yml on ZwickVitaly/CHORM

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page