Our engine never sees your warehouse password
datahashi

Semantic query syntax

JSON for Semantic Queries, filter operators, having clauses, and pagination.

Updated

A Semantic Query is the structured request accepted by POST /v1/query and the MCP tool run_semantic_query.

The body never names a model and never names a tenant. Pin a published model with version. Omit version for the current model.

Top-level query object

{
  "version": "current",
  "metrics": ["avg_order_value"],
  "measures": ["Order.revenue"],
  "dimensions": [
    "Customer.region",
    { "name": "order_date", "entity": "Order", "grain": "month" }
  ],
  "filters": [],
  "having": [],
  "segments": ["completed_orders"],
  "view": "finance_overview",
  "saved_query": "revenue_by_region",
  "order": [
    { "ref": "revenue", "desc": true }
  ],
  "limit": 100,
  "offset": 0,
  "time_zone": "Asia/Tokyo",
  "percent_of_total": ["revenue"],
  "fill_gaps": false,
  "include_sql": false
}

Field definitions

Field Type Description
version string Published model version to pin. Omit for the current model.
metrics string[] Governed metric names.
measures string[] or object[] Raw measures (Order.revenue or {name, entity}). Refused when the caller’s grants cannot select raw measures.
dimensions array A string (region or Order.region) or {name, entity, grain, relationship}.
filters array Pre-aggregation conditions. Combined with AND.
filter_group object Nested AND / OR. See below.
having array Conditions on selected aggregated columns.
segments string[] Model segments. Expanded before row-level security. Combined with filters by AND.
view string Named view. Its filters are injected. Its surface is the ceiling.
saved_query string Stored query used as the base. Sent filters and segments are added. Any other list you send replaces the stored list.
order array {ref, desc}. ref is a selected output. desc: true sorts descending.
limit integer Maximum rows. Capped by ENGINE_MAX_ROWS.
offset integer Row offset. Needs a positive limit and a non-empty order.
time_zone string IANA zone for day through year buckets of timestamp dimensions. Omit for UTC. A bad zone is 400 invalid_time_zone. A rollup never answers a non-UTC bucketed query.
percent_of_total string[] Selected additive measures returned again as a 0–1 share, in column <name>_share_of_total.
fill_gaps boolean Emit a row for every empty time bucket. Needs exactly one grouped calendar-grain dimension, no limit or offset, and an order on that dimension only.
include_sql boolean Return compiled SQL. Honoured only when ENGINE_ALLOW_SQL=true.

include_resolved is not a REST field. The MCP tool run_semantic_query accepts it. When it is true, the tool adds the normalized query that ran. That echo never includes a server-injected row-level-security predicate.

Dimension specifications

A dimension is a string or an object:

[
  "Customer.region",
  {
    "name": "order_date",
    "entity": "Order",
    "grain": "quarter"
  }
]
  • grain: minute, hour, day, week, month, quarter, fiscal_quarter, year, or fiscal_year. It must be one of the dimension’s declared grains.
  • entity: Required when the bare name exists on more than one entity. Otherwise the error reason is ambiguous_dimension or ambiguous_measure.
  • relationship: Names the join edge when two entities have more than one relationship.

Do not put Entity.name inside the object name field. A string "Order.region" is split for you. An object keeps name and entity separate.

Filter operators

Single condition

{
  "dimension": "status",
  "entity": "Order",
  "op": "eq",
  "value": "completed"
}

Supported operators

Operator Expected value
eq, ne, lt, le, gt, ge Scalar
in, not_in Non-empty array
between Two-element array [low, high], inclusive
contains, starts_with, ends_with, not_contains, not_starts_with, not_ends_with String. String dimensions only.
is_null, is_not_null null

The query API also accepts aliases such as >=, gte, ==, !=, <>, >, <, <=, and lte. Model YAML does not. Use the canonical names in YAML.

The engine binds each value as a parameter. It does not paste the value into SQL text.

Filter groups

filters is a flat list combined with AND. Nested logic is a sibling field, filter_group:

{
  "filters": [],
  "filter_group": {
    "op": "or",
    "filters": [
      { "dimension": "region", "op": "eq", "value": "emea" }
    ],
    "groups": [
      {
        "op": "and",
        "filters": [
          { "dimension": "region", "op": "eq", "value": "na" },
          { "dimension": "plan_tier", "op": "eq", "value": "enterprise" }
        ]
      }
    ]
  }
}

op is and or or. An empty op means and. Do not wrap conditions in { "or": [ ... ] } inside filters.

Having clauses

having filters run after aggregation. ref names a selected metric or measure, never a dimension.

{
  "having": [
    { "ref": "revenue", "op": "gt", "value": 50000 },
    { "ref": "order_count", "op": "ge", "value": 10 }
  ]
}

having is not valid on a rolling-window measure.

Drill-down request

Used by POST /v1/drill-down and the MCP drill_down tool. measure is a raw measure, never a metric.

{
  "measure": "Order.revenue",
  "cell": [
    { "dimension": "region", "entity": "Customer", "value": "apac" },
    { "dimension": "order_date", "entity": "Order", "grain": "month", "value": "2026-03-01" }
  ],
  "limit": 50
}

Query refinement

MCP refine_semantic_query changes an existing query and runs the merged query.

{
  "base": {
    "measures": ["Order.revenue"],
    "dimensions": ["Customer.region"]
  },
  "add_dimensions": [{ "name": "status", "entity": "Order" }],
  "add_filters": [{ "dimension": "region", "entity": "Customer", "op": "eq", "value": "na" }],
  "set_limit": 25
}

add_dimensions and add_filters append. set_metrics, set_measures, set_order, set_limit, and set_offset replace. drop_dimensions and drop_filters remove entries by dimension name. The tool always returns the resolved query.

Next: Environment variables · REST API.