Skip to content

The mental model ​

You write a SHACL shapes graph that says, in Turtle:

"Every foaf:Person must have at least one foaf:name, and the name must be a string. Every foaf:Person must have at least one foaf:mbox, and the mailbox must be an IRI."

You load the data graph and the shapes graph into pgRDF as two separate graphs. pgrdf.validate(data, shapes) returns a report listing every result, with:

  • the focus node: the node being validated;
  • the path: the property the constraint is about;
  • the value that failed, when there is one;
  • the source shape and constraint component: which rule fired;
  • the severity and a human-readable message.

The report shape ​

json
{
  "conforms": false,
  "results": [
    {
      "focusNode": "http://example.org/bob",
      "resultPath": "http://xmlns.com/foaf/0.1/mbox",
      "value": null,
      "sourceShape": "_:e0426eab19040cf3a29413efb8e7cad7",
      "sourceConstraintComponent": "http://www.w3.org/ns/shacl#MinCountConstraintComponent",
      "resultSeverity": "sh:Violation",
      "resultMessage": "MinCount(1) not satisfied"
    }
  ],
  "mode": "native",
  "data_triples": 5,
  "shapes_triples": 10,
  "data_graph_id": 5,
  "shapes_graph_id": 6,
  "elapsed_ms": 1.42
}

When the data conforms, results is empty:

json
{"conforms": true, "results": [], "mode": "native", "data_triples": 6,
 "shapes_triples": 10, "data_graph_id": 5, "shapes_graph_id": 6, "elapsed_ms": 0.72}

conforms is true only when results is empty. Any result makes it false, including one with severity sh:Warning or sh:Info. To let warnings through, filter results on resultSeverity yourself. See Report as data.

Check your graph ids ​

Strict mode (the default) refuses a shapes graph that declares no targets, so a wrong numeric shapes id fails loudly instead of passing.

A NULL id behaves differently. Like most pgRDF functions, validate returns NULL when an argument is NULL, and pgrdf.graph_id() returns NULL for an IRI that doesn't exist. A mistyped IRI therefore gives you NULL, not an error:

sql
SELECT pgrdf.validate(pgrdf.graph_id('http://example.org/data'),
                      pgrdf.graph_id('http://example.org/typo')) IS NULL;
-- true

Treat a NULL report as a failure in any gate.

Validation over inferred triples ​

Validation sees inferred triples as well as asserted ones. After pgrdf.materialize, shapes are checked against the entailed closure:

sql
SELECT pgrdf.add_graph('http://example.org/team');
SELECT pgrdf.add_graph('http://example.org/team-shapes');

SELECT pgrdf.parse_turtle('
@prefix ex:   <http://example.org/> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
ex:Engineer rdfs:subClassOf <http://xmlns.com/foaf/0.1/Person> .
ex:erin a ex:Engineer .
', pgrdf.graph_id('http://example.org/team'));

SELECT pgrdf.parse_turtle('
@prefix sh:   <http://www.w3.org/ns/shacl#> .
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
@prefix ex:   <http://example.org/> .
ex:PersonShape a sh:NodeShape ;
    sh:targetClass foaf:Person ;
    sh:property [ sh:path foaf:name ; sh:minCount 1 ] .
', pgrdf.graph_id('http://example.org/team-shapes'));

SELECT pgrdf.validate(pgrdf.graph_id('http://example.org/team'),
                      pgrdf.graph_id('http://example.org/team-shapes')) ->> 'conforms';
-- true    (erin is not yet known to be a foaf:Person)

SELECT pgrdf.materialize(pgrdf.graph_id('http://example.org/team'), 'rdfs');

SELECT pgrdf.validate(pgrdf.graph_id('http://example.org/team'),
                      pgrdf.graph_id('http://example.org/team-shapes')) ->> 'conforms';
-- false   (erin is now an inferred foaf:Person with no foaf:name)

If you validate after changing the data, check that the materialization is still current.

Validation as a gate ​

Because the report is JSONB, a pipeline can gate on the verdict:

sql
SELECT CASE WHEN coalesce((rep ->> 'conforms')::boolean, false)
            THEN 'OK'
            ELSE 'REJECT'
       END AS verdict
  FROM (SELECT pgrdf.validate(pgrdf.graph_id('http://example.org/data'),
                              pgrdf.graph_id('http://example.org/shapes')) AS rep) r;

coalesce(…, false) turns a NULL report into a rejection. A failed validation can be logged, alerted on, or used to roll back the load inside the same transaction. See Report as data.

Next: Worked example →

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