Skip to content

Worked example — subclass chain ​

A two-step rdfs:subClassOf chain you can paste into psql and watch run end to end.

Setup ​

sql
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'));
-- → 3

Three triples are asserted. Before materialization, asking for Alice's types returns only the one stated directly:

sql
SELECT * FROM pgrdf.sparql('
  PREFIX ex: <http://example.com/>
  SELECT ?c WHERE { ex:alice a ?c }');
--  {"c": "http://example.com/Engineer"}

Materialize ​

sql
SELECT jsonb_pretty(pgrdf.materialize(pgrdf.graph_id('urn:example:people')));
json
{
    "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:Person and ex:alice a ex:Agent;
  • owl:Thing typing for ex:alice, ex:Engineer and ex:Person;
  • four OWL axiomatic triples about owl:Thing and owl:Nothing.

Timings vary by machine. The field meanings are on the Reasoning page.

Query again ​

sql
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:

sql
SELECT iri, asserted, inferred, materialization
  FROM pgrdf.graph_inventory()
 WHERE iri = 'urn:example:people';
--         iri        | asserted | inferred | materialization
-- -------------------+----------+----------+-----------------
--  urn:example:people |        3 |       10 | current

export_graph returns only what you asserted, as canonical N-Triples:

sql
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:

sql
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:

sql
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.

Next: OWL 2 RL rule set →

pgRDF is released under the MIT license. Documentation built with VitePress, served via GitHub Pages.