Skip to content

check_circleCONSTRUCT — graph-shaped result rows

The canonical SPARQL form for snapshotting a subgraph, materialising a view, or feeding a graph-rewrite pipeline.

Shipped

The full CONSTRUCT surface is on the tagged v0.6.14 release: foundation + constant-only templates, variable substitution, blank-node templates (fresh-per-solution), multi-triple templates with shared per-solution bnodes, GRAPH-scoped WHERE, the WHERE-shorthand form, round-trip ingest via put_construct_rows (bnode joining preserved), and sparql_parse CONSTRUCT shape analysis. W3C-shape fixtures 30-35 lock conformance.

What it does

pgrdf.construct(q TEXT) → SETOF JSONB

For every solution of the WHERE clause, the SPARQL CONSTRUCT template is instantiated. Each instantiated triple is emitted as one JSONB row of shape:

json
{"subject": "<iri-or-bnode>", "predicate": "<iri>", "object": "<iri-or-bnode-or-literal>"}

This is directly pipeable into INSERT INTO ... SELECT, a Postgres VIEW, an INSERT DATA SPARQL UPDATE in another graph, or any tabular consumer.

Why you'd use it

  • groups Project managers — capture a subgraph as a row set you can hand to downstream tools. A snapshot, a report extract, or an ETL hand-off — one SQL call, no bespoke serialisation.
  • query_stats Data scientists — fold a graph-shaped feature into a Postgres VIEW. SELECT against the view in your existing pipelines. No leave-and-re-enter SPARQL.
  • school Ontologists — express the canonical "compute the shape, then assert it" pattern (the same one INSERT WHERE uses, but as a read with the result on the wire instead of in the graph).
  • code Backend engineers — read-only counterpart to the SPARQL UPDATE surface. Same template + WHERE pattern; result is rows, not a mutation.
  • settings Operators — observe what an INSERT WHERE would assert without committing.

The shipped surface

CapabilityDetail
pgrdf.construct(q) foundationConstant-only templates (no template variables).
Variable substitution?s ?p ?o from the WHERE solution flows into the template.
Blank-node templatesFresh-per-solution labels — _:b in the template becomes a distinct blank node per solution row.
Multi-triple templatesShared per-solution bnodes — a _:b referenced twice in the template shares one identity per solution.
GRAPH-scoped WHEREWHERE { GRAPH <iri> { ?s ?p ?o } } routes to a named graph partition.
WHERE-shorthand formThe CONSTRUCT WHERE { … } short form (template = WHERE pattern), with W3C BGP restrictions.
Round-trip ingestput_construct_rows — feed a CONSTRUCT straight back into a graph, bnode joining preserved.
sparql_parse CONSTRUCT shapeTemplate/WHERE analysis without execution.
W3C conformanceShape fixtures 30-35.

Examples

Constant-only template

sql
-- "Always emit a marker triple, once per matching subject."
SELECT * FROM pgrdf.construct(
  'PREFIX ex: <http://example.com/>
   PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
   CONSTRUCT { ex:audit rdf:type ex:Marker }
   WHERE     { ?s ex:reviewed true }');
--  → {"subject": "http://example.com/audit",
--     "predicate": "http://www.w3.org/1999/02/22-rdf-syntax-ns#type",
--     "object": "http://example.com/Marker"}
--  (one row per matching ?s)

Variable substitution

sql
-- "Every Person becomes an Agent — read-only projection."
SELECT * FROM pgrdf.construct(
  'PREFIX foaf: <http://xmlns.com/foaf/0.1/>
   PREFIX ex:   <http://example.com/>
   PREFIX rdf:  <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
   CONSTRUCT { ?p rdf:type ex:Agent }
   WHERE     { ?p rdf:type foaf:Person }');

Blank-node template — fresh per solution

sql
-- "For every person, mint a fresh contact node and attach their mailbox."
SELECT * FROM pgrdf.construct(
  'PREFIX foaf: <http://xmlns.com/foaf/0.1/>
   PREFIX ex:   <http://example.com/>
   CONSTRUCT {
     ?p ex:contact _:c .
     _:c foaf:mbox ?m .
   }
   WHERE { ?p foaf:mbox ?m }');

The two occurrences of _:c in the template share a single identity per solution row — every solution gets a fresh _:c, but within one row the same _:c is referred to twice.

GRAPH-scoped WHERE

sql
-- "Snapshot the staging graph."
SELECT * FROM pgrdf.construct(
  'CONSTRUCT { ?s ?p ?o }
   WHERE { GRAPH <http://example.org/staging> { ?s ?p ?o } }');

WHERE-shorthand

sql
-- Template equals the WHERE pattern — the W3C "CONSTRUCT WHERE {…}"
-- short form. Restricted to BGPs (no OPTIONAL/UNION/etc. inside).
SELECT * FROM pgrdf.construct(
  'PREFIX foaf: <http://xmlns.com/foaf/0.1/>
   CONSTRUCT WHERE { ?s foaf:name ?n }');

Composing with regular SQL

Because the result is a SETOF JSONB, you can fold a CONSTRUCT into a Postgres view, materialise it into a table, or pipe it into a SPARQL UPDATE in another graph:

sql
-- Materialise a CONSTRUCT into a relational table.
CREATE TABLE reports.person_agents AS
SELECT
    (t ->> 'subject')   AS person_iri,
    (t ->> 'predicate') AS predicate_iri,
    (t ->> 'object')    AS object_iri
  FROM pgrdf.construct(
    'PREFIX foaf: <http://xmlns.com/foaf/0.1/>
     PREFIX ex:   <http://example.com/>
     PREFIX rdf:  <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
     CONSTRUCT { ?p rdf:type ex:Agent }
     WHERE     { ?p rdf:type foaf:Person }') AS t;

See also

  • SPARQL UPDATE — write counterpart with the same template-from-pattern shape.
  • GRAPH clause — the inner WHERE can be graph-scoped.
  • Roadmap — the complete SPARQL surface and the v0.6 forward edge.

MIT licensed. Documentation for pgRDF — built with VitePress, served via GitHub Pages.