Skip to content

verifiedValidate ​

Check a graph against a SHACL shapes graph and get back a validation report as JSONB: data you can store, alert on, or use to gate a load.

What it is ​

Validate takes a data graph and a shapes graph and reports every constraint violation as structured JSON. The report follows the W3C SHACL result vocabulary (focus node, path, value, source shape, constraint component, severity, message), so it composes with regular SQL.

Validation sees inferred triples. Run Reason first when the shapes constrain facts that follow from the ontology.

How you run it ​

sql
pgrdf.validate(data_graph_id BIGINT, shapes_graph_id BIGINT,
               mode TEXT DEFAULT 'native', strict BOOLEAN DEFAULT true) → JSONB

The mode selects what gets evaluated:

  • 'native' (default): W3C SHACL Core, passing all 25 tests of the W3C Core suite.
  • 'pgrdf': SHACL Core and SHACL-SPARQL constraints (sh:sparql with sh:select). Use this mode when your shapes contain SPARQL constraints.
  • 'sparql': an alternative engine that does not evaluate sh:sparql. Not recommended.

Any other mode refuses with SQLSTATE 22023.

strict => true (the default) refuses to return a verdict that would mean nothing:

  • A shapes graph that declares no targets, which includes passing the wrong graph id or an empty graph. Without this check, a typo would report conforms: true.
  • Constraints the chosen mode can't evaluate, such as sh:sparql under 'native'. The error names the component and the mode that handles it.

Pass strict => false to proceed anyway, leaving those constraints unevaluated.

The report ​

json
{
  "conforms": false,
  "results": [
    {
      "focusNode": "http://example.org/alice",
      "resultPath": "http://example.org/age",
      "value": null,
      "sourceShape": "_:f25130e0…",
      "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": 1,
  "shapes_graph_id": 2,
  "elapsed_ms": 1.421415
}

That report comes from the walkthrough in Load → Validate → Query. An empty results array with "conforms": true means every focus node passed. See Report as data for querying reports with SQL.

Example — a SHACL-SPARQL constraint ​

A rule SHACL Core can't express directly: every person must be at least 18.

sql
SELECT pgrdf.add_graph('http://example.org/staff');
SELECT pgrdf.parse_turtle('
@prefix ex:   <http://example.org/> .
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
@prefix xsd:  <http://www.w3.org/2001/XMLSchema#> .

ex:bob a foaf:Person ; ex:age "30"^^xsd:integer .
ex:dan a foaf:Person ; ex:age "12"^^xsd:integer .
', pgrdf.graph_id('http://example.org/staff'));

SELECT pgrdf.add_graph('http://example.org/staff-rules');
SELECT pgrdf.parse_turtle('
@prefix ex:   <http://example.org/> .
@prefix sh:   <http://www.w3.org/ns/shacl#> .
@prefix foaf: <http://xmlns.com/foaf/0.1/> .

ex:AdultShape a sh:NodeShape ;
  sh:targetClass foaf:Person ;
  sh:sparql [
    sh:message "age must be at least 18" ;
    sh:select """
      SELECT $this ?value WHERE {
        GRAPH <http://example.org/staff> {
          $this <http://example.org/age> ?value .
          FILTER (?value < 18)
        }
      }""" ] .
', pgrdf.graph_id('http://example.org/staff-rules'));

SELECT pgrdf.validate(pgrdf.graph_id('http://example.org/staff'),
                      pgrdf.graph_id('http://example.org/staff-rules'), 'pgrdf');
json
{
  "conforms": false,
  "results": [
    {
      "focusNode": "http://example.org/dan",
      "resultPath": null,
      "value": "12",
      "sourceShape": "http://example.org/AdultShape",
      "sourceConstraintComponent": "http://www.w3.org/ns/shacl#SPARQLConstraintComponent",
      "resultSeverity": "sh:Violation",
      "resultMessage": "age must be at least 18"
    }
  ],
  "mode": "pgrdf",
  "data_triples": 4,
  "shapes_triples": 5,
  "data_graph_id": 1,
  "shapes_graph_id": 2,
  "elapsed_ms": 2.958412
}

Scope the SPARQL inside sh:select to the data graph

In 'pgrdf' mode, the query inside sh:select sees every graph in the database, not just the data graph. That can produce false positives or duplicate results. Wrap its pattern in GRAPH <data-graph-iri> { … }, as above.

The same shapes under the default mode are refused, because 'native' doesn't evaluate sh:sparql:

sql
SELECT pgrdf.validate(pgrdf.graph_id('http://example.org/staff'),
                      pgrdf.graph_id('http://example.org/staff-rules'));
-- ERROR:  validate: unenforced constraint component in shapes graph under mode "native":
--         sh:sparql (SHACL-SPARQL constraint component — use mode 'pgrdf', which evaluates it). ...

Where it sits in a chain ​

After Reason, or straight after Import; before Query or Seal. Validating after Reason checks the entailed closure, not just the asserted triples, which is often what you want. It also works as a gate on a load: validate, then accept or reject.

Scaling class — one graph, one session

Like Reason, Validate runs in one session over one graph, and that graph has to fit your hardware. For a larger source, carve a slice and validate that.

See also ​

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