Worked example — subclass chain
A two-step rdfs:subClassOf chain you can paste into psql and watch run end to end.
Setup
SELECT pgrdf.add_graph('urn:example:people');
SELECT pgrdf.parse_turtle('
@prefix ex: <http://example.com/> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
ex:Engineer rdfs:subClassOf ex:Person .
ex:Person rdfs:subClassOf ex:Agent .
ex:alice a ex:Engineer .
', pgrdf.graph_id('urn:example:people'));
-- → 3Three triples are asserted. Before materialization, asking for Alice's types returns only the one stated directly:
SELECT * FROM pgrdf.sparql('
PREFIX ex: <http://example.com/>
SELECT ?c WHERE { ex:alice a ?c }');
-- {"c": "http://example.com/Engineer"}Materialize
SELECT jsonb_pretty(pgrdf.materialize(pgrdf.graph_id('urn:example:people')));{
"diff_ms": 0.002,
"load_ms": 0.386,
"profile": "owl-rl",
"write_ms": 0.276,
"reason_ms": 0.096,
"analyze_ms": 0.202,
"elapsed_ms": 1.075,
"base_triples": 3,
"auto_analyzed": true,
"reasoner_errors": [],
"inferred_triples_written": 10,
"previous_inferred_dropped": 0
}The reasoner added 10 triples:
ex:Engineer rdfs:subClassOf ex:Agent, the transitive step;ex:alice a ex:Personandex:alice a ex:Agent;owl:Thingtyping forex:alice,ex:Engineerandex:Person;- four OWL axiomatic triples about
owl:Thingandowl:Nothing.
Timings vary by machine. The field meanings are on the Reasoning page.
Query again
SELECT * FROM pgrdf.sparql('
PREFIX ex: <http://example.com/>
SELECT ?c WHERE { ex:alice a ?c }');
-- {"c": "http://example.com/Engineer"}
-- {"c": "http://www.w3.org/2002/07/owl#Thing"}
-- {"c": "http://example.com/Person"}
-- {"c": "http://example.com/Agent"}The subclass chain is now queryable as ordinary triples. Your application can ask "is Alice an Agent?" and get yes without computing the closure itself.
See the split
graph_inventory() counts asserted and inferred triples separately:
SELECT iri, asserted, inferred, materialization
FROM pgrdf.graph_inventory()
WHERE iri = 'urn:example:people';
-- iri | asserted | inferred | materialization
-- -------------------+----------+----------+-----------------
-- urn:example:people | 3 | 10 | currentexport_graph returns only what you asserted, as canonical N-Triples:
SELECT * FROM pgrdf.export_graph(pgrdf.graph_id('urn:example:people'));
-- <http://example.com/Engineer> <http://www.w3.org/2000/01/rdf-schema#subClassOf> <http://example.com/Person> .
-- <http://example.com/Person> <http://www.w3.org/2000/01/rdf-schema#subClassOf> <http://example.com/Agent> .
-- <http://example.com/alice> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.com/Engineer> .Re-run, then refresh after an edit
Running it again replaces the inferred set. It doesn't add to it:
SELECT pgrdf.materialize(pgrdf.graph_id('urn:example:people'));
-- {..., "base_triples": 3, "inferred_triples_written": 10, "previous_inferred_dropped": 10}Add an asserted triple and the graph reads stale until you materialize again:
SELECT pgrdf.parse_turtle('@prefix ex: <http://example.com/> . ex:bob a ex:Engineer .',
pgrdf.graph_id('urn:example:people'));
SELECT materialization FROM pgrdf.graph_inventory() WHERE iri = 'urn:example:people';
-- stale
SELECT pgrdf.materialize(pgrdf.graph_id('urn:example:people'));
-- {..., "base_triples": 4, "inferred_triples_written": 13, "previous_inferred_dropped": 10}Bob picks up the same three inferred types as Alice.
See Idempotence and scheduling for running this from a scheduled job.