Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
157 changes: 149 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,30 @@
# ossie-core
# ossie

JVM library for reading, writing, and round-tripping [Apache
Ossie](https://github.com/apache/ossie) / [Open Semantic
Interchange](https://open-semantic-interchange.org/) semantic model
documents. Zero dependencies beyond Jackson.
JVM libraries for [Apache Ossie](https://github.com/apache/ossie) /
[Open Semantic Interchange](https://open-semantic-interchange.org/).

## What it does
Two artifacts:

- **[ossie-core](#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](#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
Expand All @@ -19,7 +38,7 @@ 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
### Quick start

Add the dependency (Maven Central release is pending; snapshots are
published to GitHub Packages under `spiculedata/ossie` in the meantime):
Expand Down Expand Up @@ -115,7 +134,7 @@ var doc = new OssieYamlReader()
// OssieYamlReader.SUPPORTED_VERSIONS.
```

## Compatibility
### Compatibility

- **OSI spec versions** — the DTO tree covers the v0.1.x and v0.2.x
wire formats. `OssieYamlReader.SUPPORTED_VERSIONS` names the exact
Expand All @@ -127,6 +146,128 @@ var doc = new OssieYamlReader()
- **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

```xml
<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:

```java
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

```java
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:

```java
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

- [Apache Ossie](https://github.com/apache/ossie) — the OSI
Expand Down
73 changes: 73 additions & 0 deletions ossie-core/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>bi.saiku.ossie</groupId>
<artifactId>ossie-parent</artifactId>
<version>0.1.0-SNAPSHOT</version>
</parent>

<artifactId>ossie-core</artifactId>
<packaging>jar</packaging>

<name>ossie-core</name>
<description>Zero-dependency-beyond-Jackson library for reading, writing, and round-tripping
Apache Ossie (Open Semantic Interchange) semantic model documents on the JVM.

Ships DTOs for the OSI v0.1.x / 0.2.x wire format, an OssieYamlReader that accepts YAML
*or* JSON (Jackson YAMLMapper handles both, so it consumes dbt Core 1.12's
target/osi_document.json directly), an OssieYamlWriter, an OssieSynonymIndex helper, and
optional strict version-check enforcement.

No Spring, no JAX-RS, no Mondrian, no Calcite.</description>

<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-yaml</artifactId>
</dependency>

<dependency>
<groupId>com.networknt</groupId>
<artifactId>json-schema-validator</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
</plugin>
<plugin>
<groupId>com.diffplug.spotless</groupId>
<artifactId>spotless-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
91 changes: 91 additions & 0 deletions ossie-sql/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>bi.saiku.ossie</groupId>
<artifactId>ossie-parent</artifactId>
<version>0.1.0-SNAPSHOT</version>
</parent>

<artifactId>ossie-sql</artifactId>
<packaging>jar</packaging>

<name>ossie-sql</name>
<description>Apache Calcite adapter + shelf-state query engine over Open Semantic Interchange
semantic models.

Takes an ossie-core OssieDocument + a JDBC URL for any relational warehouse (Postgres,
Snowflake, BigQuery, DuckDB, H2, MySQL, ClickHouse, ...) and gives you:

- A JDBC connection you can run raw SQL against — the OSI datasets appear as virtual
Calcite schemas / tables backed by the warehouse.
- A typed shelf-state query builder (OssieQuery) that composes rows, columns, values,
filters, sorts, and limits against the semantic model and executes them via the
same Calcite planner.

The point: agents, BI tools, and apps that want to query an Ossie semantic model against
any warehouse can depend on this library instead of reimplementing the translator + join
rule + Calcite plumbing.</description>

<dependencies>
<dependency>
<groupId>bi.saiku.ossie</groupId>
<artifactId>ossie-core</artifactId>
</dependency>
<dependency>
<groupId>org.apache.calcite</groupId>
<artifactId>calcite-core</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>

<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
</plugin>
<plugin>
<groupId>com.diffplug.spotless</groupId>
<artifactId>spotless-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Loading
Loading