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 JSONBFor every solution of the WHERE clause, the SPARQL CONSTRUCT template is instantiated. Each instantiated triple is emitted as one JSONB row of shape:
{"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 WHEREuses, 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 WHEREwould assert without committing.
The shipped surface
| Capability | Detail |
|---|---|
pgrdf.construct(q) foundation | Constant-only templates (no template variables). |
| Variable substitution | ?s ?p ?o from the WHERE solution flows into the template. |
| Blank-node templates | Fresh-per-solution labels — _:b in the template becomes a distinct blank node per solution row. |
| Multi-triple templates | Shared per-solution bnodes — a _:b referenced twice in the template shares one identity per solution. |
| GRAPH-scoped WHERE | WHERE { GRAPH <iri> { ?s ?p ?o } } routes to a named graph partition. |
| WHERE-shorthand form | The CONSTRUCT WHERE { … } short form (template = WHERE pattern), with W3C BGP restrictions. |
| Round-trip ingest | put_construct_rows — feed a CONSTRUCT straight back into a graph, bnode joining preserved. |
sparql_parse CONSTRUCT shape | Template/WHERE analysis without execution. |
| W3C conformance | Shape fixtures 30-35. |
Examples
Constant-only template
-- "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
-- "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
-- "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
-- "Snapshot the staging graph."
SELECT * FROM pgrdf.construct(
'CONSTRUCT { ?s ?p ?o }
WHERE { GRAPH <http://example.org/staging> { ?s ?p ?o } }');WHERE-shorthand
-- 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:
-- 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.