← All projects

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.duckdb or md: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()