YAML-Schemas
XML-Schemas sind präzise, aber sie bestehen aus vielen spitzen Klammern. YAML-Schemas geben Ihnen dasselbe Modell in einem Format, das auf einen Blick leichter zu lesen, in einem Pull Request leichter zu prüfen und mit #-Kommentaren leicht zu annotieren ist. Die Saiku-Engine behandelt sie identisch – der Konverter baut in beiden Fällen denselben getypten Objektgraphen.
Warum YAML?
- Lesbar. Weniger Zeichen für dieselbe Information – eine typische Dimension, die 60 XML-Zeilen einnimmt, passt in 20 YAML-Zeilen.
- Diffbar. Strukturelle Änderungen (Hinzufügen einer Ebene, Umbenennen einer Measure) erzeugen saubere, lesbare Diffs anstelle von Attribut-Suppen-Diffs.
- Kommentierbar. Sie können jeden Abschnitt mit
#-Kommentaren annotieren; XML-Kommentare sind legal, überleben aber selten Round-Trips durch grafische Tools. - Round-Trip-fähig.
XML → YAML → XMLerzeugt byte-äquivalente Abfrageergebnisse. Sie können Ihre bestehenden Schemas jederzeit mit der CLI konvertieren.
Top-Level-Struktur
Ein vollständiges M4-YAML-Schema verwendet diese Top-Level-Schlüssel (nur schema ist erforderlich):
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 (Header)
Mappt auf <Schema name="..." metamodelVersion="...">.
| Schlüssel | Erforderlich | Anmerkungen |
|---|---|---|
name | ja | Anzeigename des Schemas |
metamodel_version | nein | Typischerweise "4.0" |
Kurzform (nur Name):
schema: FoodMartLangform:
schema: name: "FoodMart" metamodel_version: "4.0"annotations
Eine flache Map von name: text-Paaren. Unterstützt auf Schema-, Cube-, Dimensions-, Hierarchie-, Level-, Attribut-, Measure-, Calculated-Member- und Rollenebene. Mondrian verwendet punktqualifizierte Namen als Konvention für sprachspezifische Metadaten:
annotations: caption.de_DE: "Verkaufen" caption.fr_FR: "Ventes" description.fr_FR: "Cube des ventes"physical_schema
Deklariert die physischen Tabellen und die Fremdschlüssel-Beziehungen zwischen ihnen.
tables
Eine Liste von Tabellendefinitionen. Jeder Eintrag mappt auf ein <Table>-Element.
| Schlüssel | Erforderlich | Anmerkungen |
|---|---|---|
name | ja | Tabellenname in der Datenbank |
alias | nein | Alternativname, der an anderer Stelle im Schema verwendet wird (z. B. für Self-Joins) |
schema | nein | Datenbankschema-Qualifizierer (z. B. dbo) |
key_column | nein | Einzelspaltiger Primärschlüssel-Kurzschreibweise |
key | nein | Mehrspaltiger Primärschlüssel – Liste von Spaltennamen-Strings |
calculated_columns | nein | Abgeleitete Spalten, definiert als SQL-Ausdrücke (siehe unten) |
key_column und key schließen sich gegenseitig aus. Verwenden Sie key_column für eine einzelne Spalte; verwenden Sie key für zusammengesetzte Schlüssel. Faktentabellen haben typischerweise keinen deklarierten Schlüssel.
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
Virtuelle Spalten, die aus SQL-Ausdrücken berechnet werden. Mappt auf <ColumnDefs><CalculatedColumnDef>.
| Schlüssel | Erforderlich | Anmerkungen |
|---|---|---|
name | ja | Spaltenname, der an anderer Stelle im Schema verwendet wird |
type | nein | Mondrian-Typ: String, Numeric, Integer |
expression | ja | Map von SQL-Dialektname → SQL-Körper |
Inline-Spaltenreferenzen in SQL-Körpern verwenden {col:column_name}- oder {col:table.column_name}-Tokens, die der Konverter zurück auf <Column>-Elemente parst:
- 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}"Unterstützte Dialekt-Schlüssel umfassen generic, mysql, oracle, postgres, mssql, access, derby, db2, luciddb.
links
Fremdschlüssel-Beziehungen zwischen Tabellen. Jeder Eintrag mappt auf ein <Link source="..." target="...">-Element.
| Schlüssel | Erforderlich | Anmerkungen |
|---|---|---|
source | ja | Kind-Tabelle (Many-Seite) |
target | ja | Eltern-Tabelle (One-Seite) |
foreign_key_column | nein* | Einzelner FK-Spaltenname (Kurzform) |
foreign_key | nein* | Liste von FK-Spaltennamen (zusammengesetzter FK) |
*Eines von foreign_key_column oder foreign_key ist erforderlich.
links: - source: "product_class" target: "product" foreign_key: - "product_class_id" - source: "store" target: "employee" foreign_key: - "store_id"shared_dimensions
Eine Map von dimension_name: dimension_body. Der Map-Schlüssel wird zum name-Attribut auf dem resultierenden <Dimension>-Element. Geteilte Dimensionen leben außerhalb eines Cubes und können von mehreren Cubes referenziert werden.
| Schlüssel | Erforderlich | Anmerkungen |
|---|---|---|
table | nein | Standardtabelle für Attribute in dieser Dimension |
key | nein | Name des Schlüsselattributs (muss mit einem Attribut-name übereinstimmen) |
type | nein | "TIME" für Zeitdimensionen; bei Standard weglassen |
attributes | nein | Liste von Attributdefinitionen |
hierarchies | nein | Liste von Hierarchiedefinitionen |
annotations | nein | Map von Annotationsname → Text |
attributes
| Schlüssel | Erforderlich | Anmerkungen |
|---|---|---|
name | ja | Anzeigename des Attributs |
table | nein | Überschreibt die Standardtabelle der Dimension |
key_column | nein* | Einzelspaltiger Schlüssel |
key | nein* | Mehrspaltiger Schlüssel – Liste von "table.column"- oder "column"-Strings |
name_column | nein | Spalte, die für den Member-Anzeigenamen verwendet wird |
name_columns | nein | Mehrspaltiger Name – Liste von Spalten-Strings |
order_by_column | nein | Spalte für die Member-Sortierung |
caption_column | nein | Spalte für die Member-Beschriftung |
level_type | nein | Zeitgranularität: TimeYears, TimeQuarters, TimeMonths, TimeWeeks, TimeDays |
datatype | nein | Boolean, Numeric, Integer, String (Standard String weglassen) |
has_hierarchy | nein | false unterdrückt die automatisch erzeugte Single-Attribut-Hierarchie (Standard true; bei true weglassen) |
hierarchy_all_member_name | nein | All-Member-Label für die auto-generierte Hierarchie |
hierarchy_all_member_caption | nein | All-Member-Caption für die auto-generierte Hierarchie |
hierarchy_default_member | nein | MDX-eindeutiger Name des Default-Members für die auto-generierte Hierarchie |
hierarchy_has_all | nein | false unterdrückt die All-Ebene in der auto-generierten Hierarchie |
properties | nein | Liste von Geschwister-Attributnamen, die Properties dieses Attributs sind |
annotations | nein | Map von Annotationsname → Text |
*key_column und key schließen sich gegenseitig aus. Für Cross-Table-Referenzen innerhalb von key oder name_columns qualifizieren Sie mit 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
| Schlüssel | Erforderlich | Anmerkungen |
|---|---|---|
name | ja | Hierarchiename |
all_member_name | nein | Label für den All-Member |
default_member | nein | MDX-eindeutiger Name des Default-Members |
has_all | nein | false unterdrückt die All-Ebene |
levels | ja | Geordnete Liste von Ebenen |
annotations | nein | Map von Annotationsname → Text |
Jeder Eintrag in levels ist entweder ein einfacher String (wenn der Ebenenname dem Attributnamen entspricht) oder eine Map {name, attribute} (wenn sie sich unterscheiden):
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"Vollständiges Beispiel – die geteilte Dimension Store aus 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
Eine Map von cube_name: cube_body. Der Map-Schlüssel wird zum name des Cubes.
| Schlüssel | Erforderlich | Anmerkungen |
|---|---|---|
default_measure | nein | Name der Standard-Measure |
annotations | nein | Map von Annotationsname → Text |
dimensions | nein | Liste von Dimensionsnutzungen und lokalen Dimensionsdefinitionen |
measure_groups | nein | Liste von Measure-Group-Definitionen |
calculated_members | nein | Liste von berechneten Member-Definitionen |
named_sets | nein | Liste von benannten Set-Definitionen |
dimensions (auf Cube-Ebene)
Jeder Eintrag ist entweder eine Nutzung (eine Referenz auf eine geteilte Dimension) oder eine lokale Definition (eine Inline-Dimension, die nur für diesen Cube definiert ist).
Nutzung – eine Map nur mit source:
dimensions: - source: "Store" - source: "Time" - source: "Product"Lokale Definition – eine Map mit name plus dem vollständigen Dimensionskörper (gleiche Schlüssel wie 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
| Schlüssel | Erforderlich | Anmerkungen |
|---|---|---|
name | nein | Name der Measure-Group; optional für die primäre Gruppe |
table | ja | Faktentabellen- oder Aggregat-Tabellenname |
type | nein | "aggregate" für aggregierte Measure-Groups; bei "fact" weglassen |
approx_row_count | nein | Hinweis für die ungefähre Faktentabellen-Zeilenanzahl (String, z. B. "86837") |
ignore_unrelated_dimensions | nein | true behandelt nicht verwandte Dimensionen als [All], statt null zurückzugeben |
measures | nein | Liste von Measure- oder Measure-Ref-Definitionen |
dimension_links | nein | Liste von Links von dieser Measure-Group zu ihren Dimensionen |
measures
Jeder Eintrag ist entweder eine Measure-Definition (hat name) oder eine Measure-Referenz (hat ref, verwendet in aggregierten Measure-Groups).
Measure-Definition:
| Schlüssel | Erforderlich | Anmerkungen |
|---|---|---|
name | ja | Anzeigename der Measure |
column | nein | Quellspalte |
aggregator | ja | sum, count, distinct-count, min, max, avg |
format_string | nein | MDX-Format-String (z. B. "#,###.00", "Standard", "Currency") |
datatype | nein | Numeric, Integer, String |
properties | nein | Liste von {name, value}- oder {name, expression}-Maps |
annotations | nein | Map von Annotationsname → Text |
Measure-Referenz (nur in aggregierten Measure-Groups):
| Schlüssel | Erforderlich | Anmerkungen |
|---|---|---|
ref | ja | Name der referenzierten Measure aus der primären Gruppe |
agg_column | nein | Spalte in der Aggregat-Tabelle mit dem vor-aggregierten Wert |
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
Jeder Link hat ein type-Feld, das den Join-Typ bestimmt.
foreign_key – der Standard-Join von der Faktentabelle zur Dimension über eine FK-Spalte:
| Schlüssel | Erforderlich | Anmerkungen |
|---|---|---|
type | ja | "foreign_key" |
dimension | ja | Dimensionsname |
foreign_key_column | nein* | Einzelner FK-Spaltenname |
foreign_key | nein* | Liste von FK-Spaltennamen (zusammengesetzter FK) |
attribute | nein | Attribut, mit dem gejoint wird, wenn der FK nicht auf den Dimensionsschlüssel zeigt |
copy – die Aggregat-Tabelle erbt Dimensionsdaten aus einer anderen Measure-Group:
| Schlüssel | Erforderlich | Anmerkungen |
|---|---|---|
type | ja | "copy" |
dimension | ja | Dimensionsname |
column_refs | nein | Liste von {table, name, agg_column}-Maps |
no_link – diese Measure-Group hat keinen Link zu dieser Dimension:
| Schlüssel | Erforderlich | Anmerkungen |
|---|---|---|
type | ja | "no_link" |
dimension | ja | Dimensionsname |
fact – die Daten der Dimension kommen direkt aus der Faktentabelle:
| Schlüssel | Erforderlich | Anmerkungen |
|---|---|---|
type | ja | "fact" |
dimension | ja | Dimensionsname |
reference – die Dimension wird indirekt über das Attribut einer anderen Dimension erreicht:
| Schlüssel | Erforderlich | Anmerkungen |
|---|---|---|
type | ja | "reference" |
dimension | ja | Dimensionsname |
via_dimension | nein | Name der Zwischendimension |
via_attribute | nein | Attribut auf der Zwischendimension, das zum Join verwendet wird |
Vollständiges Measure-Group-Beispiel:
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
| Schlüssel | Erforderlich | Anmerkungen |
|---|---|---|
name | ja | Membername |
dimension | nein | Zieldimension (z. B. "Measures") |
hierarchy | nein | MDX-eindeutiger Name der Zielhierarchie |
parent | nein | MDX-eindeutiger Name des Eltern-Members |
formula | nein | MDX-Formel |
format_string | nein | MDX-Format-String |
caption | nein | Anzeige-Caption in Client-Tools |
description | nein | Menschenlesbare Beschreibung |
visible | nein | false versteckt den Member vor Client-Tools (Standard true; bei true weglassen) |
cell_formatter | nein | Benutzerdefinierter Cell Formatter – {class_name} oder {script: {language, body}} |
properties | nein | Liste von {name, value}- oder {name, expression}-Maps |
annotations | nein | Map von Annotationsname → Text |
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
Eine Liste von Rollendefinitionen.
| Schlüssel | Erforderlich | Anmerkungen |
|---|---|---|
name | ja | Rollenname |
class_name | nein | Java-Klasse, die benutzerdefinierte Rollenlogik implementiert |
schema_grant | nein | Top-Level-Grant |
schema_grant hat access ("all", "none", "all_dimensions", "custom") und eine optionale cubes-Liste. Jeder Cube-Grant kann dimensions- und hierarchies-Listen mit eigenen Grants enthalten. Hierarchie-Grants unterstützen top_level, bottom_level, rollup_policy ("full", "partial", "hidden") und eine members-Liste.
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 im Vergleich
Hier ist dieselbe Time-Dimension in beiden Formaten:
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>Kodierung von Spaltenreferenzen
Zwei Kodierungskonventionen erscheinen im YAML-Format:
table.column-qualifizierte Referenzen – innerhalb von key- und name_columns-Listen wird eine Spalte, die zu einer nicht-Standard-Tabelle gehört, als "table_name.column_name" geschrieben. Der Konverter teilt am ersten ., um Tabelle und Spalte wiederherzustellen:
key: - "product_class.product_family" - "product_class.product_department" - "brand_name" # unqualified — belongs to the default table{col:...}-Tokens – innerhalb von SQL-Ausdruckskörpern für berechnete Spalten verwenden Inline-Spaltenreferenzen {col:column_name} oder {col:table.column_name}. Der Konverter parst diese zurück auf <Column>-Elemente:
expression: mysql: "CONCAT({col:fname}, ' ', {col:lname})" generic: "{col:fullname}"Bekannte Einschränkungen
-
Bezeichner, die einen Literal-Punkt enthalten – die
table.column-Kodierung teilt am ersten.-Zeichen. Tabellen- oder Spaltennamen, die selbst ein.enthalten (z. B. quoting Identifier), durchlaufen den Round-Trip nicht korrekt. Dasselbe gilt für{col:table.column}-Tokens. -
Whitespace in SQL berechneter Spalten – der YAML-Serialisierer kann zusätzlichen führenden Whitespace in SQL-Körpern einführen, wenn eine Datei maschinell generiert und erneut gelesen wurde. SQL mit signifikantem führenden Whitespace kann beim zweiten Round-Trip zusätzliche Einrückung erhalten.
-
$referfordert eine Datei-URL – die$ref-Include-Auflösung funktioniert nur, wenn das Schema über eineCatalog=file:///...-Connect-String-Property geladen wird. ÜberCatalogContentgeladene Schemas haben kein Basisverzeichnis, und die$ref-Auflösung wird übersprungen.
Verwandt
- CLI – Konvertierung zwischen XML und YAML und Linting von Schemas über die Kommandozeile.
- Schema-Designer – Generieren eines XML-Schemas aus einer natürlichsprachlichen Beschreibung.
- Schemas – Verwalten und Bearbeiten gespeicherter Schemas im Saiku-Dashboard.