> For the complete documentation index, see [llms.txt](https://v2.dataos.info/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://v2.dataos.info/build/productize/assets/semantic-model.md).

# Semantic model

Map physical columns to business measures, dimensions, segments, and joins so people and AI can query consistent definitions.

A semantic model is the interface between your physical tables and whoever queries them. It maps raw columns to business concepts:

* dimensions to group and filter by
* measures to aggregate
* segments to reuse as filters
* joins to relate models to each other
* rollups to pre-aggregate expensive queries

Without a semantic model, consumers need to know your table layout and write their own SQL. With one, they query governed definitions that resolve the same way every time.

The semantic layer also powers REST, GraphQL, and MySQL wire-protocol APIs.

One semantic file wraps one physical model. They live in `models/semantics/`.

## The shape

```yaml
kind: semantic
name: daily_sales
depends_on: silver.fct_daily_sales
description: Daily sales performance by customer, product, category, and region.

dimensions:
  - order_date
  - region_name
  - category
  - total_revenue

measures:
  - name: total_daily_revenue
    type: sum
    expression: "{daily_sales.total_revenue}"
    description: Total daily gross revenue

segments:
  - name: high_revenue_days
    expression: "{daily_sales.total_revenue} >= 500"
    description: Daily rows with revenue of at least 500

joins:
  - name: customer_profile
    type: many_to_one
    expression: "{daily_sales.customer_id} = {customer_profile.customer_id}"
```

The required fields are `kind: semantic`, `name` (what consumers reference: letters, digits, and underscores, starting with a letter or underscore; it must be lowercase if the model has rollups), `depends_on` (the model it wraps), and a non-empty `dimensions` list.

## The five building blocks

A semantic model is built from five kinds of objects: dimensions, measures, segments, joins, and rollups.

### Dimensions

Columns to group and filter by. List the column name, or use the full form to add descriptions, tags, and time granularities.

| Property        | Required | Description                                                                                                                                                                                   |
| --------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name`          | Yes      | Column name in the underlying model                                                                                                                                                           |
| `description`   | No       | Human readable explanation                                                                                                                                                                    |
| `tags`          | No       | Categorization labels                                                                                                                                                                         |
| `terms`         | No       | Business glossary references                                                                                                                                                                  |
| `granularities` | No       | Named time buckets for timestamp columns, each an object with `name` and `interval` (for example `name: day`, `interval: 1 day`)                                                              |
| `format`        | No       | Display hint, for example `percent`, `currency`                                                                                                                                               |
| `behavior.type` | No       | `identifier`, `categorical`, `bucketing` (numeric range buckets), or `ordinal` (ordered categories)                                                                                           |
| `public`        | No       | Whether the dimension is exposed to consumers. Defaults to `true`, except for grain dimensions Vulcan injects from the model's `grains`, which are hidden unless declared with `public: true` |

{% hint style="info" %}
Policies and masks are configured on the backing physical model, not inside semantic dimensions. Use `policies/access/*.yml` and the physical model's `column_mask_expressions`. See [Vulcan policies](https://v2.dataos.info/references/resources/vulcan/policies). If a dimension backs a rollup's `group by`, its mask must be a constant expression: a value-referencing mask is rejected.
{% endhint %}

### Measures

Named aggregations that reference columns as `{name.column}`.

| Property        | Required      | Description                                                                                                                                                           |
| --------------- | ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name`          | Yes           | Unique among measures and segments. `count` is reserved; Vulcan adds it implicitly                                                                                    |
| `type`          | Yes           | `count`, `count_distinct`, `count_distinct_approx`, `sum`, `avg`, `min`, `max`, `number`, `string`, `time`, `boolean`                                                 |
| `expression`    | Conditionally | Required for every type except `count`                                                                                                                                |
| `filters`       | No            | SQL conditions that restrict which rows are aggregated. Not allowed on `number`, `string`, `time`, or `boolean` measures                                              |
| `behavior.type` | No            | `simple`, `flow`, `stock`, `ratio`, `derived` (composed from other measures via `measure_refs`). Tells APIs and AI consumers how to interpret the measure across time |

### Segments

Reusable filter conditions. They can only reference the current model's own columns.

| Property     | Required | Description                                                     |
| ------------ | -------- | --------------------------------------------------------------- |
| `name`       | Yes      | Unique among measures and segments                              |
| `expression` | Yes      | SQL boolean condition referencing only this model's own columns |

### Joins

Relate semantic models so consumers can analyze across them. Many-to-many joins aren't supported; bridge them through an intermediate model instead.

| Property             | Required         | Description                                                                                                                                                                                                                                                                                                     |
| -------------------- | ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name`               | Yes              | Must match another semantic model's `name`                                                                                                                                                                                                                                                                      |
| `type`               | Yes              | `one_to_one`, `one_to_many`, `many_to_one`                                                                                                                                                                                                                                                                      |
| `on` or `expression` | Yes, exactly one | `on` is a structured predicate (a shared dimension name, a list for AND, a nested pair for an asymmetric column name, or `and:`/`or:` blocks) that Vulcan compiles to `expression`. Use raw `expression` (`{model_a.col} = {model_b.col}`) as an escape hatch for casts, functions, or non-equality comparisons |
| `skip_for_bi`        | No               | Excludes this direction from BI export. Required on exactly one side of a **reciprocal join** (two models joining back to each other): the join types on each side must be proper inverses                                                                                                                      |

{% hint style="warning" %}
The physical model backing every semantic model must declare `grains (...)`; a missing `grains` is always flagged. Joins depend on it too: without it, a join can't resolve cardinality and validation fails.
{% endhint %}

### Rollups

A rollup is a pre-aggregated physical table for a semantic model: instead of scanning the full base table every time, repeated aggregate queries can be served from a small precomputed table of totals by dimension and time grain. Rollups are opt-in: set `enable_rollup: true` in `config.yaml`, then add a `rollups:` block to the semantic model naming the measures, dimensions, and optional `time_dimension`/`granularity` to pre-aggregate.

## Add ai\_context for AI consumers

`ai_context` is what makes a data product safe for an AI agent to query, not just a person. It gives the model instructions, synonyms, caveats, and example queries, so natural-language questions route to the right measures. You can set it at the top level, or scoped to an individual dimension, measure, segment, join, or granularity:

```yaml
ai_context:
  instructions: >
    Daily sales model for revenue, order volume, and shipment-rate analysis.
    Grain is one order date, region, customer, and product.
  synonyms:
    - daily revenue
    - sales performance
  examples:
    - description: daily revenue by region
      format: sql
      query: |
        SELECT daily_sales.region_name, daily_sales.order_date,
               MEASURE(daily_sales.total_daily_revenue)
        FROM daily_sales GROUP BY 1, 2;
```

This context travels with the product. It's the difference between a table an AI can answer questions from, and one it just has to guess at.

## Validation and casing

Running `vulcan plan` checks that:

* `depends_on` exists
* `dimensions` is non-empty
* measure and segment references point to real columns
* join names resolve to valid semantic models
* rollups reference real measures and dimensions, and don't declare `segments:`
* schema blocks contain no unknown keys

On Snowflake, use uppercase column names throughout. Snowflake stores unquoted identifiers in uppercase.

{% hint style="warning" %}
Semantic model YAML is loaded without camelCase-to-snake\_case conversion. A camelCase key such as `dependsOn` is silently ignored rather than converted. Use `depends_on` and other snake\_case keys exactly as documented.
{% endhint %}

For the full reference, including rollups, join paths, and `ai_context` options, see [References → Vulcan → Models → Semantic Models](https://v2.dataos.info/references/resources/vulcan/models/semantic-models).


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://v2.dataos.info/build/productize/assets/semantic-model.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
