> 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

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` (lowercase, what consumers reference), `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       | Time buckets (`hour`, `day`, `week`, `month`) for timestamp columns                                 |
| `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`                                   |

{% 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 a joined semantic model must declare `grains (...)`; without it, the 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 names don't collide with column names
* segment expressions stay within the model
* join names resolve

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 dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

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

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

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.
