Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

13 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ossie

JVM libraries for Apache Ossie / Open Semantic Interchange.

Two artifacts:

  • ossie-core — DTOs + YAML/JSON reader / writer. Zero dependencies beyond Jackson. Use this when you need to read, emit, or round-trip OSI semantic model documents on the JVM.
  • ossie-sql — Apache Calcite adapter + shelf-state query engine. Point it at an OSI YAML plus a JDBC URL for any relational warehouse and get a query surface (typed shelf-state builder OR raw SQL passthrough) with cross-dataset joins, metric composition, and dialect handling.

Both are Apache 2.0, JDK 21+, and designed to be used outside the Saiku project.


ossie-core

Read, write, and round-trip Ossie semantic model documents. Zero dependencies beyond Jackson.

What it does

  • Read OSI YAML or JSON documents into a typed DTO tree. Jackson's YAMLMapper accepts both formats, so you can point it at dbt Core 1.12's target/osi_document.json unmodified.
  • Write an OSI YAML document from the same DTO tree, ready for any OSI-compatible consumer.
  • Round-trip losslessly, so a read followed by a write produces semantically identical output.

Designed to be used outside the Saiku project — no Spring, no JAX-RS, no Mondrian, no Calcite. If your JVM tool needs to understand or emit OSI semantic models, this is the smallest thing that will do it.

Quick start

Add the dependency (Maven Central release is pending; snapshots are published to GitHub Packages under spiculedata/ossie in the meantime):

<dependency>
    <groupId>bi.saiku.ossie</groupId>
    <artifactId>ossie-core</artifactId>
    <version>0.1.0</version>
</dependency>

Read a document — YAML or JSON, same call:

import bi.saiku.ossie.OssieYamlReader;
import bi.saiku.ossie.model.OssieDocument;

var doc = new OssieYamlReader().read(Paths.get("target/osi_document.json"));
System.out.println(doc.getVersion());               // "0.1.1"
System.out.println(doc.getSemanticModel().size());  // 1
System.out.println(doc.getSemanticModel().get(0).getDatasets().size());

Traverse the tree:

for (var sm : doc.getEffectiveSemanticModels()) {
    for (var ds : sm.getDatasets()) {
        System.out.println("dataset: " + ds.getName() + " → " + ds.getSource());
        for (var f : ds.getFields()) {
            System.out.println("  field: " + f.getName() + " (label: " + f.getLabel() + ")");
        }
    }
    for (var m : sm.getMetrics()) {
        System.out.println("metric: " + m.getName());
    }
}

Emit a document:

import bi.saiku.ossie.OssieYamlWriter;

var doc = new OssieDocument();
// ... build up the tree ...
new OssieYamlWriter().write(doc, Paths.get("my-model.ossie.yaml"));

Resolve AI-context synonyms to canonical entity names, without writing your own tree walk:

import bi.saiku.ossie.OssieSynonymIndex;

var model = doc.getSemanticModel().get(0);
var metrics = OssieSynonymIndex.buildMetricIndex(model);
// If the LLM (or the user) said "revenue" or "top-line" or "REVENUE",
// resolve it to the canonical metric name declared in the YAML.
String canonical = OssieSynonymIndex.resolve("revenue", metrics);
// → "net_revenue" (assuming the metric declared that synonym)

Walk the knowledge-graph half of the spec — concepts, their attributes, and their associations to other concepts. Useful for consumers that want an entity-level view of the model (an LLM prompt-builder that names the entities before drilling into their SQL representation, or a schema browser that surfaces the concept graph alongside the tables):

for (var entry : doc.getOntology()) {
    var concept = entry.getConcept();
    System.out.println("concept: " + concept.getName() +
        " (id-by: " + concept.getIdentifyBy() + ")");
    for (var rel : entry.getRelationships()) {
        System.out.println("  " + rel.getName() + " → " +
            rel.getRoles().get(0).getConcept() +
            " (" + rel.getMultiplicity() + ")");
    }
}

Reject documents at unknown spec versions — useful in CI or when you want to fail fast on drift:

var doc = new OssieYamlReader()
    .setStrictVersion(true)
    .read(Paths.get("model.ossie.yaml"));
// Throws UnsupportedOssieVersionException if the version isn't in
// OssieYamlReader.SUPPORTED_VERSIONS.

Compatibility

  • OSI spec versions — the DTO tree covers the v0.1.x and v0.2.x wire formats. OssieYamlReader.SUPPORTED_VERSIONS names the exact versions the reader knows about. Unknown top-level and per-entity fields are silently ignored (@JsonIgnoreProperties(ignoreUnknown = true)), so additive spec changes round-trip without breaking older consumers.
  • JDK — 21+.
  • dbt Core — reads target/osi_document.json from dbt 1.12 and up verbatim.

ossie-sql

Execute queries against an OSI semantic model, backed by any JDBC warehouse. Apache Calcite adapter + shelf-state query engine.

Depends on ossie-core (for the DTOs) plus Calcite. Compiles the semantic model into virtual Calcite tables at runtime; user queries authored either as typed shelf-state (OssieQuery) or as raw SQL get planned against those virtual tables, auto-joined via the relationship declarations, and executed on the underlying warehouse.

Quick start

<dependency>
    <groupId>bi.saiku.ossie</groupId>
    <artifactId>ossie-sql</artifactId>
    <version>0.1.0</version>
</dependency>

End-to-end example

Point the engine at your Ossie YAML plus the warehouse where the underlying tables live. Build a shelf-state query and execute:

import bi.saiku.ossie.sql.OssieEngine;
import bi.saiku.ossie.sql.OssieQuery;
import bi.saiku.ossie.sql.OssieResult;

try (var engine = OssieEngine.builder()
        .semanticModel(Path.of("orders.ossie.yaml"))
        .jdbcUrl("jdbc:postgresql://warehouse:5432/analytics")
        .credentials("saiku_reader", "***")
        .build()) {

    var query = OssieQuery.builder()
        .model("Orders")
        .factDataset("orders")
        .rows("customers", "customer_country")
        .values("total_revenue")
        .sortByMetric("total_revenue", "DESC")
        .limit(10)
        .build();

    OssieResult result = engine.execute(query);
    for (var row : result.getRecords()) {
        System.out.println(row);
    }
    System.out.println("Generated SQL: " + result.getGeneratedSql());
}

Preview SQL without executing

String sql = engine.compile(query);
// SELECT "customers"."COUNTRY" AS "customers.customer_country",
//        SUM("orders"."ORDER_TOTAL") AS "total_revenue"
// FROM "orders", "customers"
// GROUP BY "customers"."COUNTRY"
// ORDER BY "total_revenue" DESC
// LIMIT 10

Raw SQL over the semantic model

The engine also exposes openConnection() — a plain JDBC connection whose datasets appear as virtual tables. Any BI tool, ORM, or LLM code path that speaks JDBC gets a semantic-aware query surface:

try (var engine = OssieEngine.builder()
        .semanticModel(doc)
        .jdbcUrl("jdbc:postgresql://warehouse/analytics")
        .credentials("reader", "***")
        .build();
     var conn = engine.openConnection();
     var stmt = conn.createStatement();
     var rs = stmt.executeQuery(
         "SELECT \"customers\".\"COUNTRY\", COUNT(*) " +
         "FROM \"customers\", \"orders\" " +
         "GROUP BY \"customers\".\"COUNTRY\"")) {
    // ... iterate ...
}

customers and orders are Ossie datasets, not warehouse tables. Calcite's auto-join rule wires the join between them from the relationships block in the YAML — you don't write the ON clause.

What's supported

  • Dimensions on Rows and Columns shelves, mapped to arbitrary column expressions via field.expression.dialects.ANSI_SQL
  • Metrics with inline SQL expressions (aggregations, ratios, arbitrary scalar arithmetic — TPC-DS's SUM(sales) / COUNT(DISTINCT customer) shape works verbatim)
  • Aggregation override at query time (.values("net_revenue", "AVG"))
  • Filters: EQ, NEQ, LT, LTE, GT, GTE, IN, BETWEEN, IS_NULL, IS_NOT_NULL
  • Sorts by metric alias or by dimension
  • Row limits
  • Cross-dataset joins driven by the relationships block, injected by Calcite's planner via OssieAutoJoinRule
  • Metric views (SELECT * FROM "total_revenue" where metric names are addressable as views — useful for tools that want the aggregated columns as first-class relations)

Warehouse compatibility

Anything with a JDBC driver + a Calcite dialect: Postgres, MySQL / MariaDB, Oracle, MSSQL, DuckDB, H2, HSQLDB, Snowflake, BigQuery, ClickHouse. Dialect resolution flows through Calcite's SqlDialectFactoryImpl; unknown warehouses fall back to the ANSI-SQL dialect with a warning.


Related

Contributing

Issues and PRs welcome at github.com/spiculedata/ossie.

mvn test runs the round-trip suite. mvn verify also runs Spotless using Palantir Java Format — apply with mvn spotless:apply before opening a PR.

Licence

Apache License 2.0.

About

JVM library for reading + writing Apache Ossie / Open Semantic Interchange semantic model documents. Reads dbt Core 1.12's target/osi_document.json verbatim.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages