Sidemantic
A semantic layer for centralized metric definitions.
Define metrics once, query them anywhere. Sidemantic generates relationship-aware queries that avoid join fan-out and double-counting.
Try It
uvx sidemantic workbench --demo
Installation
uv add sidemantic
# or
pip install sidemantic
Supported Databases
Works with any SQL database. Set the connection via --connection flag or in your YAML config.
- DuckDB:
duckdb:///path/to/data.duckdbormd:my_database - PostgreSQL:
postgres://user:pass@host:5432/db - BigQuery:
bigquery://project-id/dataset - Snowflake:
snowflake://user:pass@account/db/schema - Redshift:
redshift://user:pass@host:5439/db - Databricks:
databricks://token:xxx@host/sql/warehouse - Trino:
trino://user@host:8080/catalog/schema
Query with SQL
Query your metrics using regular SQL. Sidemantic rewrites your query to use the underlying tables, handling joins and aggregations automatically.
SELECT revenue, order_count FROM orders
SELECT revenue FROM orders WHERE status = 'completed'
SELECT revenue, customer.region FROM orders
Run queries with the CLI
sidemantic query models/ --sql "SELECT revenue FROM orders"
CLI Commands
Validate definitions
sidemantic validate models/
List all models and metrics
sidemantic info models/
Export to different formats
sidemantic export models/ --format dbt
Import Existing Definitions
Already have metrics defined? Import from Cube, dbt MetricFlow, LookML, Hex, Rill, Superset, or Omni.
Auto-detect format
sidemantic import ./cube-schema/ --output models/
Import from dbt MetricFlow
sidemantic import ./dbt-models/ --format metricflow --output models/
Import from LookML
sidemantic import ./lookml/ --format lookml --output models/
Define in YAML
models:
- name: orders
table: orders
primary_key: order_id
relationships:
- name: customer
type: many_to_one
foreign_key: customer_id
dimensions:
- name: status
type: categorical
sql: status
- name: order_date
type: time
sql: created_at
granularity: day
metrics:
- name: revenue
agg: sum
sql: amount
- name: order_count
agg: count
- name: completed_revenue
agg: sum
sql: amount
filters: ["status = 'completed'"]
metrics:
- name: conversion_rate
type: ratio
numerator: orders.completed_revenue
denominator: orders.revenue
Or Define in SQL
MODEL (
name orders,
table orders,
primary_key order_id
);
RELATIONSHIP (
name customer,
type many_to_one,
foreign_key customer_id
);
DIMENSION (
name status,
type categorical,
sql status
);
DIMENSION (
name order_date,
type time,
sql created_at,
granularity day
);
METRIC (name revenue, agg sum, sql amount);
METRIC (name order_count, agg count);
Segments
models:
- name: orders
segments:
- name: completed
sql: "{model}.status = 'completed'"
- name: high_value
sql: "{model}.amount > 100"
Advanced Metrics
metrics:
# Ratio metric
- name: conversion_rate
type: ratio
numerator: orders.completed_revenue
denominator: orders.revenue
# Derived metric
- name: profit_margin
type: derived
sql: "(revenue - cost) / revenue"
# Cumulative metric
- name: running_total
type: cumulative
sql: orders.revenue
window: "7 days"
Python API
For programmatic access, use the Python API:
from sidemantic import SemanticLayer
layer = SemanticLayer.from_yaml("models/", connection="duckdb:///data.duckdb")
# Query with SQL
result = layer.sql("SELECT revenue, status FROM orders WHERE status = 'completed'")
df = result.fetchdf()
# Or use the query builder
result = layer.query(
metrics=["orders.revenue"],
dimensions=["orders.status", "customers.region"],
filters=["orders.status = 'completed'"]
)
df = result.fetchdf()