« Phase 07 · Warmup · Track Overview
Core Contributor Notes — How the Real Systems Work
Table of Contents
- 1. Apache Jena, and what a real store adds
- 2. Reasoning in production stores
- 3. Neo4j and the n10s bridge
- 4. SHACL implementations
- 5. Query planning: the thing you are actually buying
- 6. FIBO in practice
- 7. Sharp edges
- 8. What the miniature simplifies
- 9. References
1. Apache Jena, and what a real store adds
Jena is the reference Java stack and the closest thing to the lab, scaled up:
| Jena | Lab equivalent |
|---|---|
Model / Dataset | Graph |
TDB2 | (none — no persistence) |
InfModel + a reasoner | materialize |
ShaclValidator | validate |
| ARQ (the SPARQL engine) | execute |
| Fuseki | (none — no HTTP endpoint) |
Four things a real store adds that change the design rather than just the scale:
Persistence with transactions. TDB2 is MVCC with ACID transactions. That matters here for a specific reason: materialization and the assertions it derives from must be transactionally consistent, or a reader can observe a closure that reflects half an update.
Quads, not triples. Every statement lives in a named graph, so the store is a set of
(graph, subject, predicate, object). This is the mechanism behind everything
PRINCIPAL-DEEP-DIVE asks for: asserted vs derived, per-source provenance,
per-classification isolation. The lab has no quads, which is its single biggest structural
simplification.
Six index permutations rather than three, so every binding pattern has a covering index. The cost is memory; the benefit is that arbitrary user queries do not fall back to a scan.
A SPARQL endpoint (Fuseki), which turns the store into a network service — and immediately raises authentication, per-query timeouts and result-size limits as first-class concerns. A store reachable without a query timeout is a denial-of-service waiting for an unbounded property path.
2. Reasoning in production stores
Every store makes a different bet, and the differences are worth knowing before choosing:
| Store | Reasoning | Shape |
|---|---|---|
| Jena | rule reasoners (RDFS, OWL subsets) + custom rules | forward, backward, or hybrid — configurable |
| GraphDB | rulesets (RDFS, OWL-Horst, OWL 2 RL) | forward-chaining, materialized at load |
| Stardog | OWL 2 profiles + SWRL rules | query rewriting — backward, at query time |
| Neptune | none native | you materialize yourself, or use openCypher |
| RDF4J | RDFS, and a SHACL engine | forward, materialized |
Two positions worth contrasting:
GraphDB materializes at load. Fast queries, larger store, and the deletion problem is GraphDB's to solve — it does so with a retraction algorithm that is genuinely intricate. Choosing a ruleset is a deployment decision: changing it requires a reload.
Stardog rewrites queries. No stored closure, always correct under deletion, and every query pays. Its query rewriting is the practical implementation of backward chaining, and it works well precisely for the profiles (QL, RL) designed for it.
The design consequence: your reasoning strategy is largely chosen by your store, so pick the store after deciding whether write-time or read-time cost matters more. Retrofitting is a migration.
Custom rules matter in finance. Jena's rule syntax and SWRL let you express things RL cannot — "an entity is a significant controller if it controls more than 25%", which is a regulatory threshold, not a logical construct. Every store supports something like this, and it is usually where a bank's actual definitions end up living.
3. Neo4j and the n10s bridge
Neo4j is an LPG: nodes and relationships with properties. Cypher's ergonomics for path queries are genuinely better than SPARQL's for the operational case:
MATCH path = (owner:Company)-[:OWNS*1..10]->(target:Company {name: 'Acme'})
WHERE owner.sanctioned = true
RETURN owner, path
Three things Cypher does more naturally than SPARQL:
- Properties on relationships —
[:OWNS {percentage: 65, since: 2019}]— which RDF needs reification for. - Bounded variable-length paths —
*1..10— where SPARQL's+is unbounded. In a bank that bound is a safety feature, not a convenience. - Returning the path itself, which is the provenance a graph-derived claim needs.
What it does not have: formal semantics, standard validation, or federation. There is no owl:
equivalent — transitivity is something you write in a query, not something the data model knows.
n10s (neosemantics) imports RDF into Neo4j and exports back, mapping IRIs to node properties and preserving namespaces. It is the practical bridge for the hybrid position: FIBO-aligned RDF as the vocabulary of record, Neo4j for operational traversal. The caveat is that the round trip is lossy in both directions — RDF's reification and Neo4j's relationship properties do not map cleanly — so treat one as the source of truth and the other as a projection.
4. SHACL implementations
pySHACL (Python) and Jena's SHACL are the two to know. Both implement SHACL Core; both partially implement SHACL-SPARQL (constraints expressed as SPARQL queries), which is the escape hatch for anything Core cannot express.
Features beyond the lab that come up quickly:
sh:node— a shape referencing another shape, so aLegalEntityshape can require itsregisteredAddressto satisfy anAddressShape. Composition, and it is what makes shapes reusable rather than copy-pasted.sh:or,sh:and,sh:not,sh:xone— logical combination.sh:oris how you say "an LEI or a local registration number", which is the realistic version of the lab's LEI constraint.sh:targetSubjectsOf/sh:targetObjectsOf— target by property rather than class. Useful when the data has no reliable types, which is common with third-party feeds.sh:sparql— an arbitrary SPARQL constraint. Powerful and the thing that makes a shapes graph hard to review, so use it deliberately.sh:deactivated— turn a shape off without deleting it. The operational partner to severity.
Validation reports are themselves RDF. sh:ValidationReport with sh:conforms and
sh:result nodes carrying sh:focusNode, sh:resultPath, sh:sourceShape and
sh:resultSeverity. That means a report can be stored in the graph, queried with SPARQL, and
diffed over time — which is how you build "data quality over the last quarter" without a separate
system.
Advanced features (sh:rule, node expressions) let SHACL do inference as well as validation
— which blurs the clean OWL/SHACL split the WARMUP draws. The split is still the right mental
model; SHACL rules are the pragmatic escape hatch when a business rule is not a logical entailment.
5. Query planning: the thing you are actually buying
The lab evaluates patterns in written order with nested loops. A production engine does not, and the difference is the whole game.
Cardinality estimation. The engine keeps statistics — how many triples per predicate, per subject, distinct-value counts — and estimates how many bindings each pattern will produce. Then it reorders to keep intermediate result sets small.
Take the traced query from DEEP-DIVE §8:
?owner bank:controls+ ent:Acme . # unbound start: walks from every controls subject
?owner bank:onSanctionsList true . # one match in the whole graph
Written order: an expensive traversal, then a filter. Planned order: one match, then a bounded walk from a single start. On a large register that is the difference between a query that returns and one that does not — and the user wrote the same query either way.
Join algorithms. Nested-loop for small inputs, hash joins for larger ones, merge joins when both sides are sorted by the join variable — which they often are, given sorted indexes.
Path evaluation. p+ is a graph traversal, and engines implement it with bidirectional search,
memoization of visited sets across bindings, and sometimes a precomputed transitive index for
declared-transitive properties. Some let you bound it (p{1,10}), and in a bank you should.
The practical consequence for a design review: "we'll write SPARQL" is not a performance plan.
Which engine, with which statistics, and whether the hot queries have been explained are the
questions. Every serious store has an EXPLAIN; use it before going live.
6. FIBO in practice
The published artefacts are OWL files organized by module, available as RDF/XML and Turtle from the EDM Council, with a released version and a development branch.
What using it actually involves:
Namespaces are long and numerous. Every module has its own, and a FIBO import pulls in transitive dependencies. Expect the first day to be spent on prefix hygiene, and expect the graph to be substantially larger than your instance data before you have loaded a single fact.
The class hierarchy is deep and precise. FIBO distinguishes things you may not need — a
LegalEntity from a FormalOrganization from an Organization — because it models the domain
properly. The temptation is to flatten it; the discipline is to subclass at the level you actually
mean and let the hierarchy do the rest.
Reasoning over full FIBO is expensive. It uses OWL constructs beyond RL in places. Most practical deployments load the modules they need, reason with an RL ruleset, and accept that some FIBO axioms are not enforced.
Alignment beats adoption. The realistic pattern is: keep your own operational model, and map to FIBO at the boundary — for reporting, for exchange, for regulatory alignment. Full internal adoption is a multi-year programme and rarely the right first step.
The genuine value, restated: FIBO gives you an external authority for definitions. "Control means what FIBO says" is a defensible position in a way that "control means what the data team decided" is not.
7. Sharp edges
Blank nodes. RDF nodes with no IRI, used for structures like "an address with a street and a
city" where the address has no identity of its own. They complicate everything: they cannot be
referenced across graphs, their identity is scoped to a document, and SPARQL treats them
specially. The lab omits them entirely; real data is full of them, and sh:node shapes exist
largely to validate them.
owl:sameAs is a loaded weapon. It asserts two IRIs denote the same thing, and a reasoner will
merge everything said about both. One wrong sameAs from an entity-resolution pipeline
propagates through the entire closure. Keep resolution decisions in their own graph with confidence
scores, and materialize merges only above a threshold, reviewably.
Reification is awkward and you will need it. Saying "Reuters asserts that Acme is owned by Northgate, as of 2024" requires talking about a triple. RDF-star (RDF 1.2) addresses this with a cleaner syntax and is landing in stores now; before it, the options were reification (verbose) or named graphs per source (workable, and what most people do).
Unbounded property paths are a denial-of-service vector. ?x ?p+ ?y with both ends free over a
dense graph. Set a query timeout and a result limit on every endpoint, and prefer bounded paths in
anything an agent can trigger.
Materialization changes query results silently. Enabling a ruleset makes queries return more — which is correct and which will look like a bug to anyone who wrote a query against the un-reasoned graph. Version the ruleset alongside the ontology.
SPARQL FILTER placement matters semantically inside OPTIONAL. A filter inside an OPTIONAL
block constrains the optional match; outside, it constrains the whole solution and eliminates rows
with unbound variables. This trips up people who learned SQL first, and it produces silently
different results.
Literal comparison is exact, including datatype. "250000" and "250000"^^xsd:integer are
different terms. The lab tests this; real data mixes them constantly, and it is a common cause of
"the join returns nothing".
8. What the miniature simplifies
| Miniature | Reality |
|---|---|
| Triples in memory | quads, persisted, with ACID transactions and MVCC |
| Three indexes | six permutations |
| RDFS + 4 OWL rules | OWL 2 RL (~80 rules), plus custom rule languages |
| Naive forward chaining | semi-naive evaluation; or query rewriting (backward) |
| No deletion handling | retraction algorithms or truth maintenance |
| SHACL Core subset | sh:node, sh:or/and/not, sh:sparql, sh:rule, deactivation |
| Report as a Python object | sh:ValidationReport as RDF, queryable and diffable |
| No SPARQL parser | full SPARQL 1.1: UNION, MINUS, subqueries, aggregation, VALUES |
| Nested-loop joins, written order | cost-based planning with cardinality statistics |
| No blank nodes | pervasive in real data |
No owl:sameAs | entity resolution, with all its danger |
| No federation | SERVICE clauses across endpoints |
| Ten-class ontology | FIBO: thousands of classes across a dozen modules |
The reasoning transfers unchanged. What the real stack adds is persistence (and with it transactional consistency between assertions and their closure), planning (and with it the observation that join order matters more than data size), and scale (and with it semi-naive evaluation and selective materialization).
9. References
Standards
- RDF 1.1 Concepts, RDF Schema 1.1
- OWL 2 Primer, OWL 2 Profiles — the RL profile is the one production reasoners implement.
- SHACL and SHACL Advanced Features
(
sh:rule, node expressions). - SPARQL 1.1 Query — §9 property paths, §18 for the formal evaluation semantics that the lab's fold implements informally.
Implementations
- Apache Jena — TDB2, ARQ, reasoners, SHACL, Fuseki. The reference stack; its reasoner documentation is the clearest public explanation of forward/backward/hybrid.
- GraphDB — ruleset documentation, and its retraction algorithm for the deletion problem.
- Stardog — query rewriting as the alternative reasoning strategy.
- RDF4J, Amazon Neptune, Oxigraph (Rust, embeddable, good to read).
- Neo4j and n10s — the LPG side and the RDF bridge.
- pySHACL — the fullest Python SHACL implementation; read its constraint components.
FIBO
- EDM Council FIBO — modules, ontology files, and the Business Entities module for ownership and control.
Books
- Allemang & Hendler, Semantic Web for the Working Ontologist, 3rd ed.
- Robinson, Webber & Eifrem, Graph Databases — the LPG counterpoint.