Exporting to Apache Ossie
Apache Ossie (formerly Open Semantic Interchange) is an incubating Apache project defining a portable YAML/JSON specification for semantic model exchange across analytics, AI, and BI tools. Saiku ships an exporter that reads any Mondrian schema and produces a valid Ossie document — the same measure and dimension definitions become consumable by dbt, GoodData, Snowflake, Databricks, Salesforce, and every other tool with an Ossie converter.
Quick start
Pass a Mondrian XML schema in, get Ossie YAML out:
java -jar saiku-launcher/target/saiku-4.6.0.jar ossie-export \ --in saiku-home/data/Pharma.xml \ --out pharma.ossie.yamlOr use stdin/stdout for scripting:
cat schema.xml | saiku ossie-export > schema.ossie.yamlThe command exits non-zero if input can’t be read (exit 2) or output can’t be written (exit 3), with a diagnostic line on stderr. Successful runs report how many semantic models were written and (if any) which cubes were skipped:
ossie-export: wrote 1 semantic model(s) to pharma.ossie.yamlWhat maps to what
The exporter follows this mapping table 1:1. Everything except explicitly-listed exceptions lands in the target column verbatim.
| Mondrian element | Ossie output |
|---|---|
<Cube> | One semantic_model entry |
Cube’s fact <Table name="..." schema="..."/> | First dataset, source: "<schema>.<table>" |
<Dimension foreignKey="..."> with <Hierarchy><Table/> | One dim dataset per hierarchy, plus one relationship from fact.foreignKey → dim.primaryKey |
<Level name="..." column="..."/> | One field on the dim (or fact, for degenerate dims), with expression.dialects[0]=ANSI_SQL:column |
<Level ... levelType="TimeYears"> (or Quarters/Months/Days) | Field gains dimension.is_time: true |
<Measure name="..." column="..." aggregator="sum"/> | One metric with both ANSI_SQL (SUM(fact.column)) and MDX ([Measures].[Name]) dialects |
<Measure aggregator="distinct-count"> | ANSI_SQL: COUNT(DISTINCT fact.column) — plus count, min, max, avg all translate |
<CalculatedMember><Formula>...</Formula></CalculatedMember> | Metric with MDX-only expression (no reliable ANSI SQL translation for MDX formulas) |
<Annotation name="saiku.semantic.description"> | Element’s ai_context.instructions |
<Annotation name="saiku.semantic.synonyms"> | Element’s ai_context.synonyms[] (CSV split, trimmed) |
<Annotation name="saiku.semantic.pii">true | custom_extensions: [{vendor_name: SAIKU, data: '{"pii":true}'}] — JSON boolean, not string |
<Annotation name="saiku.semantic.{cardinality,grain,aggregation_kind,required_filters}"> | Same SAIKU vendor extension, values serialised as JSON strings |
Worked example — Pharma cube
Input excerpt from saiku-home/data/Pharma.xml:
<Cube name="Pharma Rx"> <Table name="fact_pharma" schema="public"/> <Dimension name="Prescriber" foreignKey="prescriberkey"> <Hierarchy hasAll="true" primaryKey="prescriberkey"> <Table name="dim_prescriber" schema="public"/> <Level name="Prescriber" column="prescriberkey" nameColumn="prescribername" type="Numeric" uniqueMembers="true"> <Annotations> <Annotation name="saiku.semantic.pii">true</Annotation> </Annotations> </Level> </Hierarchy> </Dimension> <Measure name="Quantity" column="quantity_units" aggregator="sum" formatString="#,##0"/></Cube>Produces this Ossie fragment:
version: 0.2.0.dev0semantic_model:- name: Pharma Rx datasets: - name: fact_pharma source: public.fact_pharma description: Fact table for cube 'Pharma Rx'. - name: Prescriber source: public.dim_prescriber primary_key: - prescriberkey fields: - name: Prescriber expression: dialects: - dialect: ANSI_SQL expression: prescriberkey custom_extensions: - vendor_name: SAIKU data: "{\"pii\":true}" relationships: - name: fact_pharma_to_Prescriber from: fact_pharma to: Prescriber from_columns: - prescriberkey to_columns: - prescriberkey metrics: - name: Quantity expression: dialects: - dialect: ANSI_SQL expression: SUM(fact_pharma.quantity_units) - dialect: MDX expression: "[Measures].[Quantity]"What isn’t (yet) supported
The first-cut converter handles the classic Mondrian 3–4 hybrid shape used by Pharma, Bank, and most customer schemas. It doesn’t yet handle:
- Mondrian 4
<MeasureGroups>/<Dimensions>wrapper shape. FoodMart uses this. Cubes with this shape get skipped (reported to stderr) rather than emitted as schema-invalid stubs. Follow-up work tracked on the parent Ossie/SQL epic. - Virtual cubes (
<VirtualCube>) — including our Warehouse-and-Sales example. Skipped like above. - Parent-child hierarchies. Emitted as flat levels; the hierarchical relationship isn’t representable in Ossie’s current dataset-oriented shape (the Ossie hierarchy working group is in flight — see the Ossie roadmap).
- Shared dimensions consumed via
<DimensionUsage source=...>. Only the classic per-cube<Dimension>embedding works today.
When Ossie’s hierarchy story stabilises (target: v0.3.0+) and Saiku’s Mondrian-4 MG converter lands, this table will shrink.
Consuming the output
Ossie YAML is validation-checked against apache/ossie’s osi-schema.json — every file the exporter produces round-trips through the schema with zero findings (there’s a unit test that asserts this on every commit). Downstream consumers:
- dbt — Ossie’s reference converters include a dbt module.
- Snowflake, Salesforce, GoodData, Polaris, Databricks — same directory.
- Apache Superset and Metabase — no first-party converter yet at time of writing, but the SQL-over-Ossie work on the Saiku roadmap (parent epic saiku#1387) will make Saiku itself queryable as a semantic layer via SQL.
Related
- Saiku semantic annotations — the annotation keys the exporter reads (which map into Ossie’s
ai_context+custom_extensions). - Well-known Ossie extensions — the receiving end. Once your schema exports to Ossie YAML, the
saiku.display/saiku.roles/saiku.piiwell-knowns are how you author annotations directly there. - Schema structure — where the
<Annotations>block lives inside your Mondrian schema. - apache/ossie repository — upstream spec, converters, roadmap.