Esquemas YAML
Los esquemas XML son precisos pero conllevan muchos paréntesis angulares. Los esquemas YAML le dan el mismo modelo en un formato más fácil de leer a primera vista, más fácil de revisar en un pull request y fácil de anotar con comentarios #. El motor de Saiku los trata de forma idéntica — el conversor construye el mismo grafo tipado de objetos en cualquier caso.
¿Por qué YAML?
- Legible. Menos caracteres para la misma información — una dimensión típica que ocupa 60 líneas XML cabe en 20 líneas YAML.
- Diffable. Los cambios estructurales (añadir un nivel, renombrar una medida) producen diffs limpios y legibles en lugar de diffs de sopa de atributos.
- Comentable. Puede anotar cualquier sección con comentarios
#; los comentarios XML son legales, pero rara vez sobreviven a los recorridos por herramientas gráficas. - Round-trippable.
XML → YAML → XMLproduce resultados de consulta equivalentes byte a byte. Puede convertir sus esquemas existentes en cualquier momento con el CLI.
Estructura de nivel superior
Un esquema YAML M4 completo utiliza estas claves de nivel superior (solo schema es obligatoria):
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 (cabecera)
Mapea a <Schema name="..." metamodelVersion="...">.
| Clave | Obligatoria | Notas |
|---|---|---|
name | sí | Nombre para mostrar del esquema |
metamodel_version | no | Normalmente "4.0" |
Forma corta (solo nombre):
schema: FoodMartForma larga:
schema: name: "FoodMart" metamodel_version: "4.0"annotations
Un mapa plano de pares name: text. Se admite en los niveles de esquema, cubo, dimensión, jerarquía, nivel, atributo, medida, miembro calculado y rol. Mondrian utiliza nombres calificados con puntos como convención para los metadatos específicos de la configuración regional:
annotations: caption.de_DE: "Verkaufen" caption.fr_FR: "Ventes" description.fr_FR: "Cube des ventes"physical_schema
Declara las tablas físicas y las relaciones de clave foránea entre ellas.
tables
Una lista de definiciones de tablas. Cada entrada mapea a un elemento <Table>.
| Clave | Obligatoria | Notas |
|---|---|---|
name | sí | Nombre de la tabla en la base de datos |
alias | no | Nombre alternativo usado en otras partes del esquema (p. ej. para self-joins) |
schema | no | Calificador de schema de base de datos (p. ej. dbo) |
key_column | no | Atajo de clave primaria de una sola columna |
key | no | Clave primaria multicolumna — lista de cadenas de nombres de columnas |
calculated_columns | no | Columnas derivadas definidas como expresiones SQL (véase abajo) |
key_column y key son mutuamente excluyentes. Use key_column para una sola columna; use key para claves compuestas. Las tablas de hechos no suelen tener clave 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
Columnas virtuales calculadas a partir de expresiones SQL. Mapean a <ColumnDefs><CalculatedColumnDef>.
| Clave | Obligatoria | Notas |
|---|---|---|
name | sí | Nombre de columna usado en otras partes del esquema |
type | no | Tipo de Mondrian: String, Numeric, Integer |
expression | sí | Mapa de nombre de dialecto SQL → cuerpo SQL |
Las referencias a columnas en línea dentro de los cuerpos SQL usan tokens {col:column_name} o {col:table.column_name}, que el conversor analiza para devolver 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}"Las claves de dialecto admitidas incluyen generic, mysql, oracle, postgres, mssql, access, derby, db2, luciddb.
links
Relaciones de clave foránea entre tablas. Cada entrada mapea a un elemento <Link source="..." target="...">.
| Clave | Obligatoria | Notas |
|---|---|---|
source | sí | Tabla hija (lado many) |
target | sí | Tabla padre (lado one) |
foreign_key_column | no* | Nombre de columna FK única (atajo) |
foreign_key | no* | Lista de nombres de columnas FK (FK compuesta) |
*Una de foreign_key_column o foreign_key es obligatoria.
links: - source: "product_class" target: "product" foreign_key: - "product_class_id" - source: "store" target: "employee" foreign_key: - "store_id"shared_dimensions
Un mapa de dimension_name: dimension_body. La clave del mapa se convierte en el atributo name del elemento <Dimension> resultante. Las dimensiones compartidas viven fuera de cualquier cubo y pueden ser referenciadas por varios cubos.
| Clave | Obligatoria | Notas |
|---|---|---|
table | no | Tabla por defecto para los atributos de esta dimensión |
key | no | Nombre del atributo clave (debe coincidir con un name de atributo) |
type | no | "TIME" para dimensiones de tiempo; omitir para estándar |
attributes | no | Lista de definiciones de atributos |
hierarchies | no | Lista de definiciones de jerarquías |
annotations | no | Mapa de nombre de anotación → texto |
attributes
| Clave | Obligatoria | Notas |
|---|---|---|
name | sí | Nombre para mostrar del atributo |
table | no | Sobrescribe la tabla por defecto de la dimensión |
key_column | no* | Clave de una sola columna |
key | no* | Clave multicolumna — lista de cadenas "table.column" o "column" |
name_column | no | Columna usada para el nombre para mostrar del miembro |
name_columns | no | Nombre multicolumna — lista de cadenas de columna |
order_by_column | no | Columna usada para el orden de los miembros |
caption_column | no | Columna usada para el caption del miembro |
level_type | no | Granularidad de tiempo: TimeYears, TimeQuarters, TimeMonths, TimeWeeks, TimeDays |
datatype | no | Boolean, Numeric, Integer, String (String por defecto se omite) |
has_hierarchy | no | false suprime la jerarquía de un solo atributo autogenerada (por defecto true; omitir cuando true) |
hierarchy_all_member_name | no | Etiqueta de all-member para la jerarquía autogenerada |
hierarchy_all_member_caption | no | Caption de all-member para la jerarquía autogenerada |
hierarchy_default_member | no | Nombre único MDX del miembro por defecto para la jerarquía autogenerada |
hierarchy_has_all | no | false suprime el nivel All en la jerarquía autogenerada |
properties | no | Lista de nombres de atributos hermanos que son propiedades de este atributo |
annotations | no | Mapa de nombre de anotación → texto |
*key_column y key son mutuamente excluyentes. Para referencias entre tablas dentro de key o name_columns, califíquelas con 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
| Clave | Obligatoria | Notas |
|---|---|---|
name | sí | Nombre de la jerarquía |
all_member_name | no | Etiqueta para el miembro All |
default_member | no | Nombre único MDX del miembro por defecto |
has_all | no | false suprime el nivel All |
levels | sí | Lista ordenada de niveles |
annotations | no | Mapa de nombre de anotación → texto |
Cada entrada en levels es una cadena simple (cuando el nombre del nivel coincide con el del atributo) o un mapa {name, attribute} (cuando difieren):
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"Ejemplo completo — la dimensión compartida Store de 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
Un mapa de cube_name: cube_body. La clave del mapa se convierte en el name del cubo.
| Clave | Obligatoria | Notas |
|---|---|---|
default_measure | no | Nombre de la medida por defecto |
annotations | no | Mapa de nombre de anotación → texto |
dimensions | no | Lista de usos de dimensiones y definiciones locales de dimensiones |
measure_groups | no | Lista de definiciones de grupos de medidas |
calculated_members | no | Lista de definiciones de miembros calculados |
named_sets | no | Lista de definiciones de conjuntos con nombre |
dimensions (a nivel de cubo)
Cada entrada es un uso (una referencia a una dimensión compartida) o una definición local (una dimensión en línea definida solo para este cubo).
Uso — un mapa solo con source:
dimensions: - source: "Store" - source: "Time" - source: "Product"Definición local — un mapa con name más el cuerpo completo de la dimensión (mismas claves que 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
| Clave | Obligatoria | Notas |
|---|---|---|
name | no | Nombre del grupo de medidas; opcional para el grupo principal |
table | sí | Nombre de la tabla de hechos o agregada |
type | no | "aggregate" para grupos de medidas agregados; omitir para "fact" |
approx_row_count | no | Pista para el recuento aproximado de filas de la tabla de hechos (cadena, p. ej. "86837") |
ignore_unrelated_dimensions | no | true trata las dimensiones no relacionadas como [All] en lugar de devolver null |
measures | no | Lista de definiciones de medidas o referencias de medidas |
dimension_links | no | Lista de enlaces desde este grupo de medidas a sus dimensiones |
measures
Cada entrada es una definición de medida (tiene name) o una referencia de medida (tiene ref, usada en grupos de medidas agregados).
Definición de medida:
| Clave | Obligatoria | Notas |
|---|---|---|
name | sí | Nombre para mostrar de la medida |
column | no | Columna origen |
aggregator | sí | sum, count, distinct-count, min, max, avg |
format_string | no | Cadena de formato MDX (p. ej. "#,###.00", "Standard", "Currency") |
datatype | no | Numeric, Integer, String |
properties | no | Lista de mapas {name, value} o {name, expression} |
annotations | no | Mapa de nombre de anotación → texto |
Referencia de medida (solo en grupos de medidas agregados):
| Clave | Obligatoria | Notas |
|---|---|---|
ref | sí | Nombre de la medida referenciada del grupo principal |
agg_column | no | Columna en la tabla agregada que contiene el valor preagregado |
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 enlace tiene un campo type que determina el tipo de unión.
foreign_key — la unión estándar de la tabla de hechos a la dimensión mediante una columna FK:
| Clave | Obligatoria | Notas |
|---|---|---|
type | sí | "foreign_key" |
dimension | sí | Nombre de la dimensión |
foreign_key_column | no* | Nombre de columna FK única |
foreign_key | no* | Lista de nombres de columnas FK (FK compuesta) |
attribute | no | Atributo al que unirse cuando la FK no apunta a la clave de la dimensión |
copy — la tabla agregada hereda datos de dimensión de otro grupo de medidas:
| Clave | Obligatoria | Notas |
|---|---|---|
type | sí | "copy" |
dimension | sí | Nombre de la dimensión |
column_refs | no | Lista de mapas {table, name, agg_column} |
no_link — este grupo de medidas no enlaza con esta dimensión:
| Clave | Obligatoria | Notas |
|---|---|---|
type | sí | "no_link" |
dimension | sí | Nombre de la dimensión |
fact — los datos de la dimensión provienen directamente de la tabla de hechos:
| Clave | Obligatoria | Notas |
|---|---|---|
type | sí | "fact" |
dimension | sí | Nombre de la dimensión |
reference — se accede a la dimensión indirectamente a través del atributo de otra dimensión:
| Clave | Obligatoria | Notas |
|---|---|---|
type | sí | "reference" |
dimension | sí | Nombre de la dimensión |
via_dimension | no | Nombre de la dimensión intermedia |
via_attribute | no | Atributo de la dimensión intermedia usado para unir |
Ejemplo completo de grupo de medidas:
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
| Clave | Obligatoria | Notas |
|---|---|---|
name | sí | Nombre del miembro |
dimension | no | Dimensión destino (p. ej. "Measures") |
hierarchy | no | Nombre único MDX de la jerarquía destino |
parent | no | Nombre único MDX del miembro padre |
formula | no | Fórmula MDX |
format_string | no | Cadena de formato MDX |
caption | no | Caption mostrado en las herramientas cliente |
description | no | Descripción legible para humanos |
visible | no | false oculta el miembro de las herramientas cliente (por defecto true; omitir cuando true) |
cell_formatter | no | Formateador de celdas personalizado — {class_name} o {script: {language, body}} |
properties | no | Lista de mapas {name, value} o {name, expression} |
annotations | no | Mapa de nombre de anotación → 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
Una lista de definiciones de roles.
| Clave | Obligatoria | Notas |
|---|---|---|
name | sí | Nombre del rol |
class_name | no | Clase Java que implementa la lógica personalizada del rol |
schema_grant | no | Grant de nivel superior |
schema_grant tiene access ("all", "none", "all_dimensions", "custom") y una lista opcional cubes. Cada grant de cubo puede contener listas dimensions y hierarchies con sus propios grants. Los grants de jerarquía admiten top_level, bottom_level, rollup_policy ("full", "partial", "hidden") y una 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
Aquí está la misma dimensión Time en ambos 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>Codificación de referencias de columna
Aparecen dos convenciones de codificación en el formato YAML:
Referencias calificadas table.column — dentro de las listas key y name_columns, una columna que pertenezca a una tabla no predeterminada se escribe como "table_name.column_name". El conversor divide por el primer . para recuperar tabla y columna:
key: - "product_class.product_family" - "product_class.product_department" - "brand_name" # unqualified — belongs to the default tableTokens {col:...} — dentro de los cuerpos de expresiones SQL para columnas calculadas, las referencias a columna en línea usan {col:column_name} o {col:table.column_name}. El conversor las analiza para devolver elementos <Column>:
expression: mysql: "CONCAT({col:fname}, ' ', {col:lname})" generic: "{col:fullname}"Limitaciones conocidas
-
Identificadores que contienen un punto literal — la codificación
table.columndivide en el primer carácter.. Los nombres de tabla o columna que contengan un.(p. ej. identificadores entrecomillados) no harán round-trip correctamente. Lo mismo se aplica a los tokens{col:table.column}. -
Espacios en blanco en SQL de columnas calculadas — el serializador YAML puede introducir espacios en blanco iniciales adicionales en los cuerpos SQL cuando un archivo se ha generado por máquina y se vuelve a leer. El SQL con espacios en blanco iniciales significativos puede adquirir una indentación adicional en un segundo round-trip.
-
$refrequiere una URL de archivo — la resolución de includes$refsolo funciona cuando el esquema se carga mediante una propiedad de cadena de conexiónCatalog=file:///.... Los esquemas cargados medianteCatalogContentno tienen directorio base y se omite la resolución de$ref.
Relacionado
- CLI — convertir entre XML y YAML y hacer linting de esquemas desde la línea de comandos.
- Diseñador de esquemas — generar un esquema XML a partir de una descripción en lenguaje natural.
- Esquemas — gestionar y editar esquemas guardados en el dashboard de Saiku.