Skip to content

psychologyPattern — Load → Reason → Query ​

Materialize what follows from a graph, then query the closure, so a query sees entailed facts as well as asserted ones.

When to use it ​

You need queries to return facts that follow from the ontology, such as subclass membership, transitive relations or inverses, not only the triples literally in the file. The amber Reason step runs in one session over one graph, so the graph has to fit your hardware. If it doesn't, use the carve pattern.

A worked scenario — an inverse property ​

ex:owns has an inverse, ex:ownedBy. We assert only that alice owns a book, never the inverse, and let the reasoner derive it.

Step 1 — load the data ​

sql
SELECT pgrdf.add_graph('http://example.org/library');   -- → 1
SELECT pgrdf.parse_turtle('
@prefix ex:  <http://example.org/> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .

ex:owns owl:inverseOf ex:ownedBy .
ex:alice ex:owns ex:book .
', pgrdf.graph_id('http://example.org/library'));       -- → 2

Step 2 — query before reasoning ​

Ask what alice owns by the inverse. Nothing is asserted that way yet:

sql
SELECT * FROM pgrdf.sparql(
  'PREFIX ex: <http://example.org/>
   SELECT ?s WHERE { ?s ex:ownedBy ex:alice }');
--  → (0 rows)

The inventory agrees: the graph has never been reasoned over.

sql
SELECT iri, asserted, inferred, materialization
  FROM pgrdf.graph_inventory() WHERE iri = 'http://example.org/library';
--             iri             | asserted | inferred | materialization
-- ----------------------------+----------+----------+-----------------
--  http://example.org/library |        2 |        0 | never

Step 3 — reason ​

sql
SELECT pgrdf.materialize(pgrdf.graph_id('http://example.org/library'), 'owl-rl');
--  → {"profile": "owl-rl", "base_triples": 2, "inferred_triples_written": 8,
--     "previous_inferred_dropped": 0, "reasoner_errors": [], "auto_analyzed": true,
--     "elapsed_ms": 0.962623, ...}

The OWL 2 RL prp-inv rule entails ex:book ex:ownedBy ex:alice from ex:alice ex:owns ex:book and ex:owns owl:inverseOf ex:ownedBy. The other seven inferred triples are OWL 2 RL's own axioms: alice, book and owns are typed owl:Thing, and owl:Thing and owl:Nothing are declared as classes.

Step 4 — query after reasoning ​

The same query, now answered from the closure:

sql
SELECT * FROM pgrdf.sparql(
  'PREFIX ex: <http://example.org/>
   SELECT ?s WHERE { ?s ex:ownedBy ex:alice }');
json
{"s": "http://example.org/book"}
sql
SELECT iri, asserted, inferred, materialization
  FROM pgrdf.graph_inventory() WHERE iri = 'http://example.org/library';
--             iri             | asserted | inferred | materialization
-- ----------------------------+----------+----------+-----------------
--  http://example.org/library |        2 |        8 | current

The profile changes the closure ​

owl:inverseOf is an OWL 2 RL rule, not an RDFS rule. Re-run with the RDFS profile and the inverse triple disappears:

sql
SELECT pgrdf.materialize(pgrdf.graph_id('http://example.org/library'), 'rdfs')
       ->> 'inferred_triples_written' AS inferred;
--  → 0

SELECT * FROM pgrdf.sparql(
  'PREFIX ex: <http://example.org/>
   SELECT ?s WHERE { ?s ex:ownedBy ex:alice }');
--  → (0 rows)

Each run replaces the previous inferred triples, so switching profiles is safe.

Keeping it current ​

Inferred triples are a snapshot. Add a triple and the inventory marks the materialization stale:

sql
SELECT * FROM pgrdf.sparql(
  'PREFIX ex: <http://example.org/>
   INSERT DATA { GRAPH <http://example.org/library> { ex:bob ex:owns ex:pen } }');

SELECT materialization FROM pgrdf.graph_inventory() WHERE iri = 'http://example.org/library';
--  → stale

Run materialize again, and both inverses are there:

sql
SELECT pgrdf.materialize(pgrdf.graph_id('http://example.org/library'));

SELECT * FROM pgrdf.sparql(
  'PREFIX ex: <http://example.org/>
   SELECT ?thing ?owner WHERE { ?thing ex:ownedBy ?owner } ORDER BY ?thing');
json
{"owner": "http://example.org/alice", "thing": "http://example.org/book"}
{"owner": "http://example.org/bob", "thing": "http://example.org/pen"}
sql
SELECT iri, asserted, inferred, materialization
  FROM pgrdf.graph_inventory() WHERE iri = 'http://example.org/library';
--             iri             | asserted | inferred | materialization
-- ----------------------------+----------+----------+-----------------
--  http://example.org/library |        3 |       11 | current

stale is decided by the asserted triple count. An edit that leaves the count unchanged (one delete plus one insert) still reads current, so re-run materialize after any change you care about. See Is the materialization current?.

Next step ​

Add a conformance gate over the closure with Load → Validate → Query, or right-size a large source with Ingest → Carve → Reason.

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