Semantic Query
Ask for the number. We write the SQL.
A Semantic Query is a structured object — metrics × dimensions × filters. BI tools, apps, and MCP agents send the same shape; datahashi compiles fan-out-safe SQL and runs it in your warehouse. Callers never invent joins.
Input and output
semantic-query.json
{
"metrics": ["avg_order_value"],
"dimensions": [
"Customer.region",
{ "name": "ordered_at", "entity": "Order",
"grain": "month" }
],
"filters": [
{ "dimension": "status", "entity": "Order",
"op": "eq", "value": "completed" }
],
"order": [{ "ref": "ordered_at" }],
"limit": 100
} compiled.sql
SELECT c.region,
date_trunc('month', o.ordered_at) AS ordered_at,
SUM(o.amount) / COUNT(o.id) AS avg_order_value
FROM public.orders o
JOIN public.customers c ON o.customer_id = c.id
WHERE o.status = 'completed'
GROUP BY c.region, ordered_at
ORDER BY ordered_at
LIMIT 100; The object
Request fields
- metrics
- Governed metric names, e.g. "avg_order_value". Available in both modes.
- measures
- Raw measure names, "gmv" or "Order.gmv". Explore mode only.
- dimensions
- Group-by. Each is "Entity.name" or { name, entity, grain } for a time bucket.
- filters
- { dimension, op, value } — op is eq, ne, lt, le, gt, ge, in, not_in, contains, starts_with, ends_with, is_null. Never raw SQL.
- order
- [{ ref, desc }] over the names you selected.
- view
- Restrict the query to a curated view of the model.
- limit
- Row cap. Also bounded by the workspace's cost policy.
- version
- Pin a published model version. Defaults to the current one.
What you can rely on
Joins found for you
Ask for a measure on one entity and a dimension on another; the path between them is worked out. Callers — BI tools, apps, and MCP agents — never invent joins.
No double-counting
A measure joined across a one-to-many relationship isn't inflated. Fan-out-safe SQL runs in your warehouse — counts of distinct things stay correct.
Averages that match finance
avg_order_value is revenue / order_count on the aggregated rows — never avg(amount) row by row, which weights big orders wrong.
The request never changes
BI, apps, and MCP agents send the same structured object. It compiles to Snowflake, BigQuery, Postgres, MySQL, or DuckDB SQL. Change warehouses and nothing that queries you has to be rewritten.
Errors
Stable codes, JSON envelope
Every query error is { "error": { "code", "message" } } with a stable code you can branch on.
| HTTP | Code | When |
|---|---|---|
| 400 | bad_request | invalid grain, operator, type mismatch, ambiguity |
| 401 | unauthenticated | missing or invalid bearer token |
| 403 | forbidden | a governed credential asked for raw measures |
| 404 | not_found | unknown metric, measure, or dimension |
| 429 | over_budget | the query exceeds the workspace cost policy |
| 503 | overloaded | the service is at capacity |