Examples

Basic Query

models:
  - name: orders
    table: orders
    primary_key: id
    metrics:
      - name: revenue
        agg: sum
        expr: amount
from sidemantic import SemanticLayer, Model, Metric

layer = SemanticLayer()

orders = Model(
    name="orders",
    table="orders",
    primary_key="id",
    metrics=[
        Metric(name="revenue", agg="sum", expr="amount")
    ]
)

layer.add_model(orders)

Query with SQL:

layer.sql("SELECT revenue FROM orders")

Or with native Python API:

# Get SQL only
sql = layer.compile(metrics=["orders.revenue"])

# Execute and get results
result = layer.query(metrics=["orders.revenue"])
df = result.fetchdf()

Filtering and Grouping

layer.sql("""
    SELECT revenue, status
    FROM orders
    WHERE status IN ('completed', 'shipped')
""")
TipNo GROUP BY Needed

The semantic layer automatically groups by dimensions. Just select what you want!

Time Dimensions

dimensions:
  - name: order_date
    type: time
    expr: order_date
    granularity: day
from sidemantic import Dimension

Dimension(
    name="order_date",
    type="time",
    expr="order_date",
    granularity="day"
)
# Automatic time grouping
layer.sql("""
    SELECT revenue, order_date
    FROM orders
""")

Cross-Model Queries

models:
  - name: orders
    table: orders
    primary_key: id
    relationships:
      - name: customers
        type: many_to_one
        foreign_key: customer_id

  - name: customers
    table: customers
    primary_key: id
    dimensions:
      - name: region
        type: categorical
        expr: region
from sidemantic import Model, Relationship, Dimension

orders = Model(
    name="orders",
    table="orders",
    primary_key="id",
    relationships=[
        Relationship(name="customers", type="many_to_one", foreign_key="customer_id")
    ]
)

customers = Model(
    name="customers",
    table="customers",
    primary_key="id",
    dimensions=[
        Dimension(name="region", type="categorical", expr="region")
    ]
)
# Automatic join
layer.sql("""
    SELECT orders.revenue, customers.region
    FROM orders
""")

Segments (Named Filters)

models:
  - name: orders
    segments:
      - name: completed
        sql: "{model}.status = 'completed'"
      - name: high_value
        sql: "{model}.amount >= 500"
from sidemantic import Segment

orders = Model(
    name="orders",
    segments=[
        Segment(name="completed", sql="{model}.status = 'completed'"),
        Segment(name="high_value", sql="{model}.amount >= 500")
    ]
)
# Use segments in queries
layer.compile(
    metrics=["orders.revenue"],
    segments=["orders.completed", "orders.high_value"]
)

Metric-Level Filters

metrics:
  - name: completed_revenue
    agg: sum
    expr: amount
    filters:
      - "{model}.status = 'completed'"
Metric(
    name="completed_revenue",
    agg="sum",
    expr="amount",
    filters=["{model}.status = 'completed'"]
)

Filters are automatically applied in the WHERE clause whenever the metric is used.

Ratio Metrics

metrics:
  - name: completed_revenue
    agg: sum
    expr: amount
    filters: ["{model}.status = 'completed'"]

  - name: total_revenue
    agg: sum
    expr: amount

  - name: completion_rate
    type: ratio
    numerator: completed_revenue
    denominator: total_revenue
Metric(name="completed_revenue", agg="sum", expr="amount",
       filters=["{model}.status = 'completed'"]),
Metric(name="total_revenue", agg="sum", expr="amount"),
Metric(name="completion_rate", type="ratio",
       numerator="completed_revenue", denominator="total_revenue")
layer.sql("SELECT completion_rate FROM orders")

Derived Metrics

metrics:
  - name: revenue
    agg: sum
    expr: amount

  - name: cost
    agg: sum
    expr: cost

  - name: profit
    type: derived
    expr: "revenue - cost"

  - name: margin
    type: derived
    expr: "profit / revenue"
Metric(name="revenue", agg="sum", expr="amount"),
Metric(name="cost", agg="sum", expr="cost"),
Metric(name="profit", type="derived", expr="revenue - cost"),
Metric(name="margin", type="derived", expr="profit / revenue")
layer.sql("SELECT revenue, profit, margin FROM orders")

Cumulative Metrics

metrics:
  - name: daily_revenue
    agg: sum
    expr: amount

  - name: running_total
    type: cumulative
    expr: daily_revenue
    window: "7 days"

  - name: mtd_revenue
    type: cumulative
    expr: daily_revenue
    grain_to_date: month
Metric(name="daily_revenue", agg="sum", expr="amount"),
Metric(name="running_total", type="cumulative",
       expr="daily_revenue", window="7 days"),
Metric(name="mtd_revenue", type="cumulative",
       expr="daily_revenue", grain_to_date="month")
layer.sql("""
    SELECT order_date, running_total, mtd_revenue
    FROM orders
""")

Relative Date Ranges

# Use natural language date ranges in filters
layer.compile(
    metrics=["orders.revenue"],
    filters=["orders.order_date >= 'last 7 days'"]
)

# Other examples:
# - "today"
# - "yesterday"
# - "last 30 days"
# - "this week"
# - "this month"
# - "this quarter"
# - "this year"

Hierarchies & Drill-Down

dimensions:
  - name: country
    type: categorical
    expr: country

  - name: state
    type: categorical
    expr: state
    parent: country

  - name: city
    type: categorical
    expr: city
    parent: state
Dimension(name="country", type="categorical", expr="country"),
Dimension(name="state", type="categorical", expr="state", parent="country"),
Dimension(name="city", type="categorical", expr="city", parent="state")
# Use drill-down API
model.get_hierarchy_path("city")  # ['country', 'state', 'city']
model.get_drill_down("state")     # 'city'
model.get_drill_up("city")        # 'state'

Inheritance

models:
  - name: base_orders
    table: orders
    dimensions:
      - name: status
        expr: status

  - name: completed_orders
    extends: base_orders
    metrics:
      - name: revenue
        agg: sum
        expr: amount
base = Model(
    name="base_orders",
    table="orders",
    dimensions=[Dimension(name="status", expr="status")]
)

extended = Model(
    name="completed_orders",
    extends="base_orders",
    metrics=[Metric(name="revenue", agg="sum", expr="amount")]
)

Child models inherit all dimensions, metrics, and relationships from parent.

Jinja2 Templates

metrics:
  - name: filtered_revenue
    agg: sum
    expr: |
      {% if include_tax %}
        amount * 1.1
      {% else %}
        amount
      {% endif %}
Metric(
    name="filtered_revenue",
    agg="sum",
    expr="""
    {% if include_tax %}
        amount * 1.1
    {% else %}
        amount
    {% endif %}
    """
)
layer.compile(
    metrics=["orders.filtered_revenue"],
    parameters={"include_tax": True}
)

Ungrouped Queries

# Get raw rows without aggregation
layer.compile(
    metrics=["orders.revenue"],  # Still available
    dimensions=["orders.order_id", "orders.customer_id"],
    ungrouped=True  # No GROUP BY
)

Parameterized Queries

parameters:
  - name: start_date
    type: date
    default_value: "2024-01-01"

  - name: min_amount
    type: number
    default_value: 100
from sidemantic import Parameter

Parameter(name="start_date", type="date", default_value="2024-01-01"),
Parameter(name="min_amount", type="number", default_value=100)
layer.sql(
    "SELECT revenue FROM orders WHERE order_date >= {{ start_date }}",
    parameters={"start_date": "2024-06-01"}
)
WarningParameter Syntax

DON’T add quotes around { param } - they’re added automatically based on type: - ❌ WHERE date >= '{{ start_date }}' (double quotes!) - ✅ WHERE date >= {{ start_date }}

Multi-Hop Joins

models:
  - name: orders
    relationships:
      - name: customers
        type: many_to_one
        foreign_key: customer_id

  - name: customers
    relationships:
      - name: regions
        type: many_to_one
        foreign_key: region_id

  - name: regions
    dimensions:
      - name: country
        type: categorical
        expr: country
orders = Model(
    name="orders",
    relationships=[
        Relationship(name="customers", type="many_to_one", foreign_key="customer_id")
    ]
)

customers = Model(
    name="customers",
    relationships=[
        Relationship(name="regions", type="many_to_one", foreign_key="region_id")
    ]
)

regions = Model(
    name="regions",
    dimensions=[
        Dimension(name="country", type="categorical", expr="country")
    ]
)
# Spans 2 hops: orders -> customers -> regions
layer.sql("""
    SELECT orders.revenue, regions.country
    FROM orders
""")

Multi-Format Loading

Load semantic models from different formats (Cube, LookML, Hex, MetricFlow, etc.) all at once:

from sidemantic import SemanticLayer, load_from_directory

layer = SemanticLayer(connection="duckdb:///data.db")
load_from_directory(layer, "semantic_models/")

# Query across all formats seamlessly
result = layer.query(
    metrics=["orders.revenue", "products.avg_price"],
    dimensions=["customers.region", "products.category"]
)

load_from_directory() automatically: - Discovers all .lkml, .yml, .yaml files - Detects the format and parses with the right adapter - Infers relationships from foreign key naming (customer_idcustomers) - Builds the join graph

See the multi_format_demo for a complete working example with Cube, Hex, and LookML.