Saltearse al contenido

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 → XML produce 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="...">.

ClaveObligatoriaNotas
nameNombre para mostrar del esquema
metamodel_versionnoNormalmente "4.0"

Forma corta (solo nombre):

schema: FoodMart

Forma 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>.

ClaveObligatoriaNotas
nameNombre de la tabla en la base de datos
aliasnoNombre alternativo usado en otras partes del esquema (p. ej. para self-joins)
schemanoCalificador de schema de base de datos (p. ej. dbo)
key_columnnoAtajo de clave primaria de una sola columna
keynoClave primaria multicolumna — lista de cadenas de nombres de columnas
calculated_columnsnoColumnas 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 declared

calculated_columns

Columnas virtuales calculadas a partir de expresiones SQL. Mapean a <ColumnDefs><CalculatedColumnDef>.

ClaveObligatoriaNotas
nameNombre de columna usado en otras partes del esquema
typenoTipo de Mondrian: String, Numeric, Integer
expressionMapa 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.

Relaciones de clave foránea entre tablas. Cada entrada mapea a un elemento <Link source="..." target="...">.

ClaveObligatoriaNotas
sourceTabla hija (lado many)
targetTabla padre (lado one)
foreign_key_columnno*Nombre de columna FK única (atajo)
foreign_keyno*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.

ClaveObligatoriaNotas
tablenoTabla por defecto para los atributos de esta dimensión
keynoNombre del atributo clave (debe coincidir con un name de atributo)
typeno"TIME" para dimensiones de tiempo; omitir para estándar
attributesnoLista de definiciones de atributos
hierarchiesnoLista de definiciones de jerarquías
annotationsnoMapa de nombre de anotación → texto

attributes

ClaveObligatoriaNotas
nameNombre para mostrar del atributo
tablenoSobrescribe la tabla por defecto de la dimensión
key_columnno*Clave de una sola columna
keyno*Clave multicolumna — lista de cadenas "table.column" o "column"
name_columnnoColumna usada para el nombre para mostrar del miembro
name_columnsnoNombre multicolumna — lista de cadenas de columna
order_by_columnnoColumna usada para el orden de los miembros
caption_columnnoColumna usada para el caption del miembro
level_typenoGranularidad de tiempo: TimeYears, TimeQuarters, TimeMonths, TimeWeeks, TimeDays
datatypenoBoolean, Numeric, Integer, String (String por defecto se omite)
has_hierarchynofalse suprime la jerarquía de un solo atributo autogenerada (por defecto true; omitir cuando true)
hierarchy_all_member_namenoEtiqueta de all-member para la jerarquía autogenerada
hierarchy_all_member_captionnoCaption de all-member para la jerarquía autogenerada
hierarchy_default_membernoNombre único MDX del miembro por defecto para la jerarquía autogenerada
hierarchy_has_allnofalse suprime el nivel All en la jerarquía autogenerada
propertiesnoLista de nombres de atributos hermanos que son propiedades de este atributo
annotationsnoMapa 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: false

hierarchies

ClaveObligatoriaNotas
nameNombre de la jerarquía
all_member_namenoEtiqueta para el miembro All
default_membernoNombre único MDX del miembro por defecto
has_allnofalse suprime el nivel All
levelsLista ordenada de niveles
annotationsnoMapa 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.

ClaveObligatoriaNotas
default_measurenoNombre de la medida por defecto
annotationsnoMapa de nombre de anotación → texto
dimensionsnoLista de usos de dimensiones y definiciones locales de dimensiones
measure_groupsnoLista de definiciones de grupos de medidas
calculated_membersnoLista de definiciones de miembros calculados
named_setsnoLista 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

ClaveObligatoriaNotas
namenoNombre del grupo de medidas; opcional para el grupo principal
tableNombre de la tabla de hechos o agregada
typeno"aggregate" para grupos de medidas agregados; omitir para "fact"
approx_row_countnoPista para el recuento aproximado de filas de la tabla de hechos (cadena, p. ej. "86837")
ignore_unrelated_dimensionsnotrue trata las dimensiones no relacionadas como [All] en lugar de devolver null
measuresnoLista de definiciones de medidas o referencias de medidas
dimension_linksnoLista 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:

ClaveObligatoriaNotas
nameNombre para mostrar de la medida
columnnoColumna origen
aggregatorsum, count, distinct-count, min, max, avg
format_stringnoCadena de formato MDX (p. ej. "#,###.00", "Standard", "Currency")
datatypenoNumeric, Integer, String
propertiesnoLista de mapas {name, value} o {name, expression}
annotationsnoMapa de nombre de anotación → texto

Referencia de medida (solo en grupos de medidas agregados):

ClaveObligatoriaNotas
refNombre de la medida referenciada del grupo principal
agg_columnnoColumna 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"

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:

ClaveObligatoriaNotas
type"foreign_key"
dimensionNombre de la dimensión
foreign_key_columnno*Nombre de columna FK única
foreign_keyno*Lista de nombres de columnas FK (FK compuesta)
attributenoAtributo 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:

ClaveObligatoriaNotas
type"copy"
dimensionNombre de la dimensión
column_refsnoLista de mapas {table, name, agg_column}

no_link — este grupo de medidas no enlaza con esta dimensión:

ClaveObligatoriaNotas
type"no_link"
dimensionNombre de la dimensión

fact — los datos de la dimensión provienen directamente de la tabla de hechos:

ClaveObligatoriaNotas
type"fact"
dimensionNombre de la dimensión

reference — se accede a la dimensión indirectamente a través del atributo de otra dimensión:

ClaveObligatoriaNotas
type"reference"
dimensionNombre de la dimensión
via_dimensionnoNombre de la dimensión intermedia
via_attributenoAtributo 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

ClaveObligatoriaNotas
nameNombre del miembro
dimensionnoDimensión destino (p. ej. "Measures")
hierarchynoNombre único MDX de la jerarquía destino
parentnoNombre único MDX del miembro padre
formulanoFórmula MDX
format_stringnoCadena de formato MDX
captionnoCaption mostrado en las herramientas cliente
descriptionnoDescripción legible para humanos
visiblenofalse oculta el miembro de las herramientas cliente (por defecto true; omitir cuando true)
cell_formatternoFormateador de celdas personalizado — {class_name} o {script: {language, body}}
propertiesnoLista de mapas {name, value} o {name, expression}
annotationsnoMapa 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.

ClaveObligatoriaNotas
nameNombre del rol
class_namenoClase Java que implementa la lógica personalizada del rol
schema_grantnoGrant 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"

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 table

Tokens {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

  1. Identificadores que contienen un punto literal — la codificación table.column divide 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}.

  2. 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.

  3. $ref requiere una URL de archivo — la resolución de includes $ref solo funciona cuando el esquema se carga mediante una propiedad de cadena de conexión Catalog=file:///.... Los esquemas cargados mediante CatalogContent no 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.