Schemas YAML
Schemas XML são precisos mas são muitos colchetes angulares. Schemas YAML te dão o mesmo modelo em um formato que é mais fácil de ler à primeira vista, mais fácil de revisar em um pull request e fácil de anotar com comentários #. O engine do Saiku os trata identicamente — o conversor constrói o mesmo grafo de objetos tipado de qualquer maneira.
Por que YAML?
- Legível. Menos caracteres para a mesma informação — uma dimensão típica que toma 60 linhas XML cabe em 20 linhas YAML.
- Diffable. Mudanças estruturais (adicionar um level, renomear uma medida) produzem diffs limpos e legíveis em vez de diffs de sopa de atributo.
- Comentável. Você pode anotar qualquer seção com comentários
#; comentários XML são legais mas raramente sobrevivem round-trips por ferramentas gráficas. - Round-trippable.
XML → YAML → XMLproduz resultados de consulta byte-equivalentes. Você pode converter seus schemas existentes a qualquer momento com a CLI.
Estrutura de nível superior
Um schema YAML M4 completo usa essas chaves de nível superior (apenas schema é obrigatório):
schema: # required — schema header name: "FoodMart" metamodel_version: "4.0"
annotations: # optional — schema-level metadata caption.de_DE: "Verkaufen"
physical_schema: # optional — tables, calculated columns, links tables: [...] links: [...]
shared_dimensions: # optional — named dimensions reused across cubes Store: { ... } Time: { ... }
cubes: # optional — one entry per cube Sales: { ... }
roles: # optional — access-control roles - name: "California manager" schema_grant: { ... }schema (cabeçalho)
Mapeia para <Schema name="..." metamodelVersion="...">.
| Chave | Obrigatório | Notas |
|---|---|---|
name | sim | Nome de exibição do schema |
metamodel_version | não | Tipicamente "4.0" |
Forma curta (apenas nome):
schema: FoodMartForma longa:
schema: name: "FoodMart" metamodel_version: "4.0"annotations
Um mapa plano de pares name: text. Suportado nos níveis de schema, cube, dimension, hierarchy, level, attribute, measure, calculated member e role. O Mondrian usa nomes qualificados com ponto como convenção para metadados específicos de locale:
annotations: caption.de_DE: "Verkaufen" caption.fr_FR: "Ventes" description.fr_FR: "Cube des ventes"physical_schema
Declara as tabelas físicas e as relações de foreign key entre elas.
tables
Uma lista de definições de tabela. Cada entrada mapeia para um elemento <Table>.
| Chave | Obrigatório | Notas |
|---|---|---|
name | sim | Nome da tabela no banco |
alias | não | Nome alternativo usado em outro lugar no schema (ex.: para self-joins) |
schema | não | Qualificador de schema do banco (ex.: dbo) |
key_column | não | Shorthand de primary key de coluna única |
key | não | Primary key multi-coluna — lista de strings de nome de coluna |
calculated_columns | não | Colunas derivadas definidas como expressões SQL (veja abaixo) |
key_column e key são mutuamente exclusivos. Use key_column para coluna única; use key para chaves compostas. Tabelas de fato tipicamente não têm chave declarada.
physical_schema: tables: - name: "customer" key: - "customer_id" - name: "product" key_column: "product_id" - name: "salary" alias: "salary2" # second alias for a self-join - name: "sales_fact_1997" # fact table — no key declaredcalculated_columns
Colunas virtuais computadas de expressões SQL. Mapeia para <ColumnDefs><CalculatedColumnDef>.
| Chave | Obrigatório | Notas |
|---|---|---|
name | sim | Nome de coluna usado em outro lugar no schema |
type | não | Tipo Mondrian: String, Numeric, Integer |
expression | sim | Mapa de nome de dialeto SQL → body SQL |
Referências de coluna inline dentro de bodies SQL usam tokens {col:column_name} ou {col:table.column_name}, que o conversor parseia de volta para elementos <Column>:
- name: "customer" key: - "customer_id" calculated_columns: - name: "full_name" type: "String" expression: oracle: "{col:fname} || ' ' || {col:lname}" mysql: "CONCAT({col:fname}, ' ', {col:lname})" mssql: "{col:fname} + ' ' + {col:lname}" generic: "{col:fullname}"Chaves de dialeto suportadas incluem generic, mysql, oracle, postgres, mssql, access, derby, db2, luciddb.
links
Relações de foreign key entre tabelas. Cada entrada mapeia para um elemento <Link source="..." target="...">.
| Chave | Obrigatório | Notas |
|---|---|---|
source | sim | Tabela filha (lado many) |
target | sim | Tabela pai (lado one) |
foreign_key_column | não* | Nome da coluna FK única (shorthand) |
foreign_key | não* | Lista de nomes de coluna FK (FK composta) |
*Um de foreign_key_column ou foreign_key é obrigatório.
links: - source: "product_class" target: "product" foreign_key: - "product_class_id" - source: "store" target: "employee" foreign_key: - "store_id"shared_dimensions
Um mapa de dimension_name: dimension_body. A chave do mapa se torna o atributo name no elemento <Dimension> resultante. Shared dimensions vivem fora de qualquer cubo e podem ser referenciadas por múltiplos cubos.
| Chave | Obrigatório | Notas |
|---|---|---|
table | não | Tabela default para atributos nesta dimensão |
key | não | Nome do atributo chave (deve casar com um name de atributo) |
type | não | "TIME" para dimensões de tempo; omita para padrão |
attributes | não | Lista de definições de atributo |
hierarchies | não | Lista de definições de hierarquia |
annotations | não | Mapa de nome de annotation → texto |
attributes
| Chave | Obrigatório | Notas |
|---|---|---|
name | sim | Nome de exibição do atributo |
table | não | Sobrescreve a tabela default da dimensão |
key_column | não* | Chave de coluna única |
key | não* | Chave multi-coluna — lista de strings "table.column" ou "column" |
name_column | não | Coluna usada para o nome de exibição do membro |
name_columns | não | Nome multi-coluna — lista de strings de coluna |
order_by_column | não | Coluna usada para ordenação de membro |
caption_column | não | Coluna usada para o caption do membro |
level_type | não | Grão de tempo: TimeYears, TimeQuarters, TimeMonths, TimeWeeks, TimeDays |
datatype | não | Boolean, Numeric, Integer, String (default String é omitido) |
has_hierarchy | não | false suprime a hierarquia de atributo único auto-gerada (default true; omita quando true) |
hierarchy_all_member_name | não | Rótulo do membro all para a hierarquia auto-gerada |
hierarchy_all_member_caption | não | Caption do membro all para a hierarquia auto-gerada |
hierarchy_default_member | não | Nome único MDX do membro default para a hierarquia auto-gerada |
hierarchy_has_all | não | false suprime o level All na hierarquia auto-gerada |
properties | não | Lista de nomes de atributos irmãos que são properties deste atributo |
annotations | não | Mapa de nome de annotation → texto |
*key_column e key são mutuamente exclusivos. Para referências cross-table dentro de key ou name_columns, qualifique com table.column:
attributes: - name: "Brand Name" table: "product" key: - "product_class.product_family" # qualified cross-table ref - "product_class.product_department" - "brand_name" # unqualified — belongs to default table name_column: "brand_name" has_hierarchy: falsehierarchies
| Chave | Obrigatório | Notas |
|---|---|---|
name | sim | Nome da hierarquia |
all_member_name | não | Rótulo para o membro All |
default_member | não | Nome único MDX do membro default |
has_all | não | false suprime o level All |
levels | sim | Lista ordenada de levels |
annotations | não | Mapa de nome de annotation → texto |
Cada entrada em levels é uma string nua (quando o nome do level é igual ao nome do atributo) ou um mapa {name, attribute} (quando diferem):
hierarchies: - name: "Stores" all_member_name: "All Stores" levels: - "Store Country" # bare string - "Store State" - "Store City" - "Store Name"
- name: "Education Level" levels: - name: "Education Level" # map form — level name differs from attribute attribute: "Education"Exemplo completo — a shared dimension Store do FoodMart:
shared_dimensions: Store: table: "store" key: "Store Id" attributes: - name: "Store Country" key_column: "store_country" has_hierarchy: false - name: "Store State" key_column: "store_state" has_hierarchy: false - name: "Store City" key: - "store_state" - "store_city" name_column: "store_city" has_hierarchy: false - name: "Store Id" key_column: "store_id" has_hierarchy: false - name: "Store Name" key_column: "store_name" has_hierarchy: false properties: - "Store Type" - "Store Manager" - name: "Store Type" key_column: "store_type" hierarchy_all_member_name: "All Store Types" hierarchies: - name: "Stores" all_member_name: "All Stores" levels: - "Store Country" - "Store State" - "Store City" - "Store Name"cubes
Um mapa de cube_name: cube_body. A chave do mapa se torna o name do cubo.
| Chave | Obrigatório | Notas |
|---|---|---|
default_measure | não | Nome da medida default |
annotations | não | Mapa de nome de annotation → texto |
dimensions | não | Lista de usos de dimensão e definições de dimensão locais |
measure_groups | não | Lista de definições de measure group |
calculated_members | não | Lista de definições de calculated member |
named_sets | não | Lista de definições de named set |
dimensions (nível de cubo)
Cada entrada é um uso (uma referência a uma shared dimension) ou uma definição local (uma dimensão inline definida apenas para este cubo).
Uso — um mapa com apenas source:
dimensions: - source: "Store" - source: "Time" - source: "Product"Definição local — um mapa com name mais o body completo da dimensão (mesmas chaves de shared_dimensions):
dimensions: - name: "Customer" table: "customer" key: "Name" attributes: - name: "Country" key_column: "country" has_hierarchy: false - name: "Name" key_column: "customer_id" name_column: "full_name" order_by_column: "full_name" has_hierarchy: false hierarchies: - name: "Customers" all_member_name: "All Customers" levels: - "Country" - "State Province" - "City" - "Name"measure_groups
| Chave | Obrigatório | Notas |
|---|---|---|
name | não | Nome do measure group; opcional para o grupo primário |
table | sim | Nome de tabela de fato ou agregada |
type | não | "aggregate" para measure groups agregados; omita para "fact" |
approx_row_count | não | Dica para a contagem aproximada de linhas da tabela de fato (string, ex.: "86837") |
ignore_unrelated_dimensions | não | true trata dimensões não relacionadas como [All] em vez de retornar null |
measures | não | Lista de definições de medida ou referência de medida |
dimension_links | não | Lista de links deste measure group para suas dimensões |
measures
Cada entrada é uma definição de medida (tem name) ou uma referência de medida (tem ref, usada em measure groups agregados).
Definição de medida:
| Chave | Obrigatório | Notas |
|---|---|---|
name | sim | Nome de exibição da medida |
column | não | Coluna fonte |
aggregator | sim | sum, count, distinct-count, min, max, avg |
format_string | não | Format string MDX (ex.: "#,###.00", "Standard", "Currency") |
datatype | não | Numeric, Integer, String |
properties | não | Lista de mapas {name, value} ou {name, expression} |
annotations | não | Mapa de nome de annotation → texto |
Referência de medida (apenas measure groups agregados):
| Chave | Obrigatório | Notas |
|---|---|---|
ref | sim | Nome da medida referenciada do grupo primário |
agg_column | não | Coluna na tabela agregada segurando o valor pré-agregado |
measures: - name: "Unit Sales" column: "unit_sales" aggregator: "sum" format_string: "Standard"
- name: "Customer Count" column: "customer_id" aggregator: "distinct-count" format_string: "#,###"
# Inside an aggregate measure group: - ref: "Unit Sales" agg_column: "unit_sales_sum"
- ref: "Fact Count" agg_column: "fact_count"dimension_links
Cada link tem um campo type que determina o tipo de join.
foreign_key — o join padrão da tabela de fato para a dimensão via uma coluna FK:
| Chave | Obrigatório | Notas |
|---|---|---|
type | sim | "foreign_key" |
dimension | sim | Nome da dimensão |
foreign_key_column | não* | Nome da coluna FK única |
foreign_key | não* | Lista de nomes de coluna FK (FK composta) |
attribute | não | Atributo ao qual juntar quando a FK não aponta para a chave da dimensão |
copy — a tabela agregada herda dados de dimensão de outro measure group:
| Chave | Obrigatório | Notas |
|---|---|---|
type | sim | "copy" |
dimension | sim | Nome da dimensão |
column_refs | não | Lista de mapas {table, name, agg_column} |
no_link — este measure group não liga a esta dimensão:
| Chave | Obrigatório | Notas |
|---|---|---|
type | sim | "no_link" |
dimension | sim | Nome da dimensão |
fact — os dados da dimensão vêm diretamente da tabela de fato:
| Chave | Obrigatório | Notas |
|---|---|---|
type | sim | "fact" |
dimension | sim | Nome da dimensão |
reference — a dimensão é alcançada indiretamente via outro atributo de dimensão:
| Chave | Obrigatório | Notas |
|---|---|---|
type | sim | "reference" |
dimension | sim | Nome da dimensão |
via_dimension | não | Nome da dimensão intermediária |
via_attribute | não | Atributo na dimensão intermediária usado para juntar |
Exemplo completo de measure group:
measure_groups: - name: "Sales" table: "sales_fact_1997" measures: - name: "Unit Sales" column: "unit_sales" aggregator: "sum" format_string: "Standard" - name: "Store Cost" column: "store_cost" aggregator: "sum" format_string: "#,###.00" - name: "Customer Count" column: "customer_id" aggregator: "distinct-count" format_string: "#,###" dimension_links: - type: "foreign_key" dimension: "Store" foreign_key_column: "store_id" - type: "foreign_key" dimension: "Time" foreign_key_column: "time_id" - type: "foreign_key" dimension: "Product" foreign_key_column: "product_id"
- table: "agg_c_special_sales_fact_1997" type: "aggregate" measures: - ref: "Fact Count" agg_column: "fact_count" - ref: "Unit Sales" agg_column: "unit_sales_sum" dimension_links: - type: "foreign_key" dimension: "Store" foreign_key_column: "store_id" - type: "copy" dimension: "Time"calculated_members
| Chave | Obrigatório | Notas |
|---|---|---|
name | sim | Nome do membro |
dimension | não | Dimensão alvo (ex.: "Measures") |
hierarchy | não | Nome único MDX da hierarquia alvo |
parent | não | Nome único MDX do membro pai |
formula | não | Fórmula MDX |
format_string | não | Format string MDX |
caption | não | Caption de exibição mostrado em ferramentas cliente |
description | não | Descrição legível |
visible | não | false esconde o membro de ferramentas cliente (default true; omita quando true) |
cell_formatter | não | Cell formatter customizado — {class_name} ou {script: {language, body}} |
properties | não | Lista de mapas {name, value} ou {name, expression} |
annotations | não | Mapa de nome de annotation → texto |
calculated_members: - name: "Profit" dimension: "Measures" formula: "[Measures].[Store Sales] - [Measures].[Store Cost]" properties: - name: "FORMAT_STRING" value: "$#,##0.00"
- name: "Profit Growth" dimension: "Measures" formula: >- ([Measures].[Profit] - [Measures].[Profit last Period]) / [Measures].[Profit last Period] properties: - name: "FORMAT_STRING" value: "0.0%"named_sets
named_sets: - name: "Top Sellers" formula: "TopCount([Warehouse].[Warehouse Name].MEMBERS, 5, [Measures].[Warehouse Sales])"roles
Uma lista de definições de role.
| Chave | Obrigatório | Notas |
|---|---|---|
name | sim | Nome da role |
class_name | não | Classe Java implementando lógica customizada de role |
schema_grant | não | Grant de nível superior |
schema_grant tem access ("all", "none", "all_dimensions", "custom") e uma lista cubes opcional. Cada grant de cubo pode conter listas dimensions e hierarchies com seus próprios grants. Hierarchy grants suportam top_level, bottom_level, rollup_policy ("full", "partial", "hidden") e uma lista members.
roles: - name: "California manager" schema_grant: access: "none" cubes: - cube: "Sales" access: "all" dimensions: - dimension: "Gender" access: "none" hierarchies: - hierarchy: "[Store].[Stores]" access: "custom" top_level: "[Store].[Stores].[Store Country]" members: - member: "[Store].[Stores].[USA].[CA]" access: "all" - member: "[Store].[Stores].[USA].[CA].[Los Angeles]" access: "none"XML vs YAML lado a lado
Aqui está a mesma dimensão Time em ambos os formatos:
shared_dimensions: Time: table: "time_by_day" key: "Time Id" type: "TIME" attributes: - name: "Year" key_column: "the_year" level_type: "TimeYears" has_hierarchy: false - name: "Quarter" key: - "the_year" - "quarter" name_column: "quarter" level_type: "TimeQuarters" has_hierarchy: false - name: "Month" key: - "the_year" - "month_of_year" name_column: "the_month" level_type: "TimeMonths" has_hierarchy: false - name: "Time Id" key_column: "time_id" has_hierarchy: false hierarchies: - name: "Time" has_all: false levels: - "Year" - "Quarter" - "Month"<Dimension name='Time' table='time_by_day' key='Time Id' type='TIME'> <Attributes> <Attribute name='Year' keyColumn='the_year' levelType='TimeYears' hasHierarchy='false'/> <Attribute name='Quarter' levelType='TimeQuarters' nameColumn='quarter' hasHierarchy='false'> <Key> <Column name='the_year'/> <Column name='quarter'/> </Key> </Attribute> <Attribute name='Month' levelType='TimeMonths' nameColumn='the_month' hasHierarchy='false'> <Key> <Column name='the_year'/> <Column name='month_of_year'/> </Key> </Attribute> <Attribute name='Time Id' keyColumn='time_id' hasHierarchy='false'/> </Attributes> <Hierarchies> <Hierarchy name='Time' hasAll='false'> <Level attribute='Year'/> <Level attribute='Quarter'/> <Level attribute='Month'/> </Hierarchy> </Hierarchies></Dimension>Codificação de referência de coluna
Duas convenções de codificação aparecem ao longo do formato YAML:
Referências qualificadas table.column — dentro de listas key e name_columns, uma coluna pertencente a uma tabela não default é escrita como "table_name.column_name". O conversor divide no primeiro . para recuperar tabela e coluna:
key: - "product_class.product_family" - "product_class.product_department" - "brand_name" # unqualified — belongs to the default tableTokens {col:...} — dentro de bodies de expressão SQL para colunas calculadas, referências de coluna inline usam {col:column_name} ou {col:table.column_name}. O conversor as parseia de volta para elementos <Column>:
expression: mysql: "CONCAT({col:fname}, ' ', {col:lname})" generic: "{col:fullname}"Limitações conhecidas
-
Identificadores contendo um ponto literal — a codificação
table.columndivide no primeiro caractere.. Nomes de tabela ou coluna que contêm um.(ex.: identificadores quotados) não vão fazer round-trip corretamente. O mesmo se aplica a tokens{col:table.column}. -
Espaço em branco em SQL de coluna calculada — o serializador YAML pode introduzir espaço em branco inicial extra em bodies SQL quando um arquivo foi gerado por máquina e lido de novo. SQL com espaço em branco inicial significativo pode adquirir indentação extra em um segundo round-trip.
-
$refexige uma URL de arquivo — a resolução de include$refsó funciona quando o schema é carregado via uma propriedade de connect-stringCatalog=file:///.... Schemas carregados viaCatalogContentnão têm diretório base e a resolução$refé pulada.
Relacionado
- CLI — converta entre XML e YAML e faça lint de schemas pela linha de comando.
- Schema designer — gere um schema XML a partir de uma descrição em linguagem natural.
- Schemas — gerencie e edite schemas salvos no dashboard do Saiku.