OBSL — RDF Graph & SPARQL¶
OBSL (OrionBelt Semantic Layer vocabulary) is an RDF-based exchange format for semantic-layer models. When you load a model, OrionBelt automatically exports it as an OBSL-Core 0.2 RDF graph. You can retrieve the graph as Turtle or run read-only SPARQL queries against it — no extra setup required.
What is OBSL-Core?¶
OBSL-Core 0.2 maps every OBML concept to RDF triples using standard vocabularies:
| OBML Concept | RDF Class | Key Properties |
|---|---|---|
| Model container | obsl:SemanticModel |
obsl:hasDataObject, obsl:hasDimension, obsl:hasMeasure, obsl:hasMetric |
| Data Object | obsl:DataObject |
obsl:code, obsl:database, obsl:schema, obsl:hasColumn, obsl:hasJoin |
| Column | obsl:Column |
obsl:code, obsl:resultType |
| Join | obsl:Join |
obsl:joinTo, obsl:cardinality, obsl:columnFrom, obsl:columnTo |
| Dimension | obsl:Dimension |
obsl:dataObject, obsl:column, obsl:resultType, obsl:timeGrain |
| Measure | obsl:Measure |
obsl:aggregation, obsl:resultType, obsl:sourceColumn, obsl:expressionSource, obsl:filterExpression, obsl:grainMode, obsl:grainInclude, obsl:filterContextMode, obsl:owner, obsl:dataType, obsl:format |
| Metric | obsl:Metric |
obsl:metricType, obsl:expressionSource, obsl:baseMeasure, obsl:referencesMeasure, obsl:owner, obsl:dataType, obsl:format |
| Cumulative Metric | obsl:CumulativeMetric |
obsl:timeDimension, obsl:cumulativeType, obsl:window, obsl:grainToDate |
| Period-over-Period Metric | obsl:PeriodOverPeriodMetric |
obsl:timeDimension, obsl:timeGrain, obsl:offset, obsl:offsetGrain, obsl:comparison |
| Reaggregate Metric | obsl:ReaggregateMetric |
obsl:per, obsl:aggregation, obsl:reaggregateHaving (one per having condition), obsl:baseMetric (when its measure is another reaggregate metric) |
| External concept mapping | obsl:ExternalConceptMapping (plus a direct skos:*Match triple on the artefact) |
obsl:sourceObject, obsl:targetConcept, obsl:authoredConcept, obsl:mappingRelation, obsl:mappingJustification, obsl:mappingSource, obsl:ontologyVersion, obsl:confidence, obsl:mappingComment |
Labels use rdfs:label, synonyms use obsl:synonym, and descriptions use rdfs:comment.
External concept mappings in the graph¶
An externalConceptMappings entry becomes a direct SKOS mapping triple from the artefact to the expanded external IRI, so any SKOS-aware consumer reads it without knowing OBSL:
<https://ralforion.com/ns/model/sales/measure/revenue>
skos:exactMatch <https://ontology.example.com/business/NetRevenue> .
exact is skos:exactMatch, close is skos:closeMatch, broader is skos:broadMatch, narrower is skos:narrowMatch, related is skos:relatedMatch. A mapping that carries provenance (justification, source, ontology version, confidence, comment) additionally gets an obsl:ExternalConceptMapping resource holding it, at a deterministic IRI under the artefact. The model's ontology.prefixes and skos are bound on the graph, so a serialized Turtle shows corp:NetRevenue the way the author wrote it. The external ontology itself is never imported: the graph references its IRIs and says nothing about them.
Namespace
Vocabulary reference: https://ralforion.com/ns/obsl/Retrieving the Graph¶
After loading a model, retrieve its RDF graph as Turtle:
The response is text/turtle:
@prefix obsl: <https://ralforion.com/ns/obsl#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
<https://ralforion.com/ns/model/abc123> a obsl:SemanticModel ;
obsl:hasDataObject <.../data-object/orders> ;
obsl:hasDimension <.../dimension/country> ;
obsl:hasMeasure <.../measure/revenue> .
<.../measure/revenue> a obsl:Measure ;
rdfs:label "Revenue" ;
obsl:aggregation "sum" ;
obsl:resultType "float" ;
obsl:expressionSource "{[Orders].[Price]} * {[Orders].[Quantity]}" .
SPARQL Queries¶
The Gradio UI has a SPARQL tab with a gallery of the examples below, run against whatever model is loaded in the editor.
Run read-only SPARQL (SELECT and ASK) against any loaded model:
curl -X POST http://localhost:8000/v1/sessions/{session_id}/models/{model_id}/sparql \
-H "Content-Type: application/json" \
-d '{"query": "PREFIX obsl: <https://ralforion.com/ns/obsl#> PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> SELECT ?label WHERE { ?m a obsl:Measure ; rdfs:label ?label . }"}'
SELECT example¶
List all measures with their aggregation:
PREFIX obsl: <https://ralforion.com/ns/obsl#>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
SELECT ?label ?agg WHERE {
?m a obsl:Measure ;
rdfs:label ?label ;
obsl:aggregation ?agg .
}
Response:
{
"type": "select",
"variables": ["label", "agg"],
"results": [
{"label": "Revenue", "agg": "sum"},
{"label": "Order Count", "agg": "count"}
],
"boolean": null
}
ASK example¶
Check if any dimension exists:
Response:
More query ideas¶
# Find all joins and their cardinality
PREFIX obsl: <https://ralforion.com/ns/obsl#>
SELECT ?from ?to ?card WHERE {
?j a obsl:Join ;
obsl:joinTo ?to ;
obsl:cardinality ?card .
?from obsl:hasJoin ?j .
}
# Find all synonyms across the model
PREFIX obsl: <https://ralforion.com/ns/obsl#>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
SELECT ?label ?synonym WHERE {
?x rdfs:label ?label ;
obsl:synonym ?synonym .
}
# Find metrics that reference a specific measure
PREFIX obsl: <https://ralforion.com/ns/obsl#>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
SELECT ?metric ?measure WHERE {
?m a obsl:Metric ;
rdfs:label ?metric ;
obsl:referencesMeasure ?ref .
?ref rdfs:label ?measure .
}
# Every modeled artefact by type and label
PREFIX obsl: <https://ralforion.com/ns/obsl#>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
SELECT ?type ?label WHERE {
?x a ?type ;
rdfs:label ?label .
FILTER(?type IN (obsl:DataObject, obsl:Dimension, obsl:Measure, obsl:Metric))
}
ORDER BY ?type ?label
# Measures and the physical columns they aggregate
PREFIX obsl: <https://ralforion.com/ns/obsl#>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
SELECT ?measure ?aggregation ?column WHERE {
?m a obsl:Measure ;
rdfs:label ?measure ;
obsl:aggregation ?aggregation ;
obsl:sourceColumn ?c .
?c obsl:code ?column .
}
# Which artefacts link into one external namespace, and with what provenance?
PREFIX obsl: <https://ralforion.com/ns/obsl#>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX skos: <http://www.w3.org/2004/02/skos/core#>
SELECT ?label ?relation ?concept ?justification ?source WHERE {
?x rdfs:label ?label ;
?relation ?concept .
FILTER(?relation IN (skos:exactMatch, skos:closeMatch, skos:broadMatch,
skos:narrowMatch, skos:relatedMatch))
FILTER(STRSTARTS(STR(?concept), "https://example.com/ontology/commerce/"))
OPTIONAL {
?x obsl:hasExternalConceptMapping ?mapping .
?mapping obsl:targetConcept ?concept ;
obsl:mappingJustification ?justification ;
obsl:mappingSource ?source .
}
}
ORDER BY ?label
# What does each measure mean in the corporate ontology?
# externalConceptMappings become direct skos:*Match triples from the
# element to the external IRI; one with provenance also has an
# obsl:ExternalConceptMapping resource (source, justification, confidence).
PREFIX obsl: <https://ralforion.com/ns/obsl#>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX skos: <http://www.w3.org/2004/02/skos/core#>
SELECT ?measure ?relation ?concept WHERE {
?m a obsl:Measure ;
rdfs:label ?measure ;
?relation ?concept .
FILTER(?relation IN (skos:exactMatch, skos:closeMatch, skos:broadMatch,
skos:narrowMatch, skos:relatedMatch))
}
Unbound variables are warned about
ORDER BY ?lable for a query that binds ?label is valid SPARQL: an unbound variable compares equal everywhere, so nothing is ordered and no engine complains. Since that is nearly always a typo, the endpoint (and the UI) returns a warnings entry for a variable that is ordered by or projected but never bound in a triple pattern, BIND or VALUES.
Read-only
Only SELECT and ASK queries are allowed. Update operations (INSERT, DELETE, LOAD, DROP, etc.) return HTTP 400.
How It Works¶
The OBSL graph is generated eagerly at model load time — there is no extra step to trigger it. When you call POST /v1/sessions/{id}/models to load a model, the graph is built and cached alongside the SemanticModel. Subsequent /graph and /sparql calls read from this cache, so they are fast.
The graph is removed when the model is unloaded (DELETE /v1/sessions/{id}/models/{mid}).
Specification¶
The full OBSL-Core 0.2 specification — including all classes, properties, URI strategy, OBML mapping, and controlled value sets — is in ontology/spec.md.
The OWL ontology (obsl.ttl), SHACL shapes (obsl.shacl.ttl), and a Sales model example (example-sales.ttl) are available in the ontology/ directory and at https://ralforion.com/ns/obsl/.
The same files are published at https://ralforion.com/ns/obsl/obsl.ttl, obsl.shacl.ttl and example-sales.ttl, next to the vocabulary page; the docs deploy copies them from ontology/ on every release, so the published copy is the repository's. The version IRI https://ralforion.com/ns/obsl/0.2 resolves to a version page with the same downloads.