Models
Models are project-owned resources that transform permitted project sources. They form the stable boundary between physical input data and the semantic layer; their refreshed output is materialized analytical state.
A LeapView Model is a logical resource, not a physical table. A refresh creates a model materialization for that resource; documentation may call the resulting physical relation a materialized table when storage details matter. Neither physical term changes the resource's identity as a Model.
Raw inputs often contain transport-oriented names, weakly typed values, duplicate records, or joins that should not be repeated for every chart. A Model makes that cleanup and shaping explicit once.
Contract
A Model declares named identity entities (primary, unique, or foreign), a selected grain entity, source dependencies, documented output fields, and a SQL transformation:
apiVersion: leapview.dev/v1
kind: Model
metadata:
id: model:orders
name: orders
displayName: Orders
spec:
definition:
type: sql
sql: |
SELECT order_id, try_cast(order_purchase_timestamp AS DATE) AS purchase_date,
CAST(0 AS DECIMAL(38, 2)) AS revenue
FROM source."olist.orders"
WHERE order_id IS NOT NULL
entities:
order:
type: primary
fields: [order_id]
grain:
entity: order
fields:
order_id: {datatype: String, label: Order ID}
purchase_date: {datatype: Date, label: Purchase date}
revenue: {datatype: Decimal, label: Revenue}
The generated Model configuration is the exact field reference. Governed source/model relations can feed a transformation, and the compiler derives the resulting lineage so downstream semantic models see every exposed field.
Authored contract metadata
Source and Model resources may declare metadata.contract with a semantic
version and compatibility: backward. Source schema fields and Model fields
support nullable, tags, criticalDataElement, classification,
authoritativeDefinitions, and deprecation. These are authored contract and
governance inputs, not authorization grants or a change to runtime nullability
inference. An authoritative definition records a type (businessDefinition or
transformationImplementation) and an absolute URI. Deprecation records a
semantic since version, a reason, and an optional replacement field identifier.
The current generated schema checks the URI's scheme/non-whitespace shape,
not full URI syntax or whether the referenced definition exists.
Every authored Model check requires an id, unique within that Model, in
addition to its existing type-specific fields. Checks may also carry descriptive
description and tags metadata. Keep the ID stable when editing a check;
changing its list position does not change its authored identity.
Accepting this metadata does not calculate a canonical contract digest, enforce publication compatibility, or authorize deployment. Those remain separate contract-projection and delivery responsibilities.
Grain and identity entities
The grain states what one row represents through grain.entity; identity entities declare the ordered field tuple that identifies or relates those rows. Use type: primary for the row identity, type: unique for alternate identities, and type: foreign for relationships to another entity. For example:
entities:
order:
type: primary
fields: [order_id]
order_line:
type: unique
fields: [order_id, product_id]
grain:
entity: order_line
This makes composite identity explicit instead of encoding it as a scalar grain value. Do not join a one-to-many dimension into an order-grain Model without deciding how the join changes grain. Duplicate rows will inflate sums and counts later, even if individual previews look plausible. Validate entity uniqueness, nullability, and expected row counts during development.
What belongs in a Model
Good Model work includes:
- parsing timestamps and numeric strings into stable types;
- normalizing identifiers and missing values;
- deduplicating records according to a documented rule;
- joining source records needed by most consumers;
- deriving reusable physical columns;
- reducing expensive raw inputs to a supported analytical grain.
Business aggregations such as revenue, active customers, or conversion rate generally belong in semantic metrics. Keep them out of Model SQL unless the Model's declared grain itself is aggregated.
Source namespace
Transform SQL reads permitted project sources through the source namespace. Quoted names are important when source IDs contain dots. The compiler derives lineage from governed SQL and keeps refresh planning accurate.
Refresh and activation
Materialization builds replacement analytical state away from active serving state. A successful refresh validates and activates the new state. A failed or cancelled refresh leaves existing queries on the previous usable state.
That boundary does not make transformations automatically safe. A valid SQL statement can still produce the wrong grain, unexpected nulls, or an empty materialized output. Preview inputs, inspect output, and compare invariants before promoting a change.
Design checklist
Before exposing a Model to the semantic layer, confirm:
- its name and field IDs are stable;
- one sentence can describe the grain;
- the primary entity fields are non-null and unique at the selected grain;
- field types do not depend on accidental source inference;
- every source dependency is referenced through the governed SQL namespace and appears in compiler-derived lineage;
- expensive repeated work is materialized once;
- the transformation has a bounded and understandable failure mode.
See Define models for the full workflow and Materialization and refresh for operations.