Examples
Basic Query
models:
- name: orders
table: orders
primary_key: id
metrics:
- name: revenue
agg: sum
expr: amountfrom 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')
""")The semantic layer automatically groups by dimensions. Just select what you want!
Time Dimensions
dimensions:
- name: order_date
type: time
expr: order_date
granularity: dayfrom 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: regionfrom 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_revenueMetric(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: monthMetric(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: stateDimension(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: amountbase = 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: 100from 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"}
)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: countryorders = 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_id → customers) - Builds the join graph
See the multi_format_demo for a complete working example with Cube, Hex, and LookML.