rocket_launchCONSTRUCT — graph-shaped result rows
The canonical SPARQL form for snapshotting a subgraph, materialising a view, or feeding a graph-rewrite pipeline. Currently shipping on
main— Phase D, pre-release.
In flight
This surface is landing on the pgRDF main branch right now (Phase D countdown slices 54-59 — pgrdf.construct(q) foundation, variable substitution, blank-node templates, multi-triple templates, GRAPH-scoped WHERE, WHERE-shorthand). It will tag in the next release (post-v0.4.3). The shape described below is what's on main today.
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.
Surface as it stands on main
The slice countdown reveals the order capabilities landed:
| Phase D slice | Feature |
|---|---|
| 59 | pgrdf.construct(q) foundation with constant-only templates (no template variables). |
| 58 | Variable substitution in templates — ?s ?p ?o from the WHERE solution flows into the template. |
| 57 | Blank-node templates with fresh-per-solution labels — _:b in the template becomes a distinct blank node per solution row. |
| 56 | Multi-triple templates with shared per-solution bnodes — a _:b referenced twice in the template shares one identity per solution. |
| 55 | GRAPH-scoped WHERE — WHERE { GRAPH <iri> { ?s ?p ?o } } routes to a named graph partition. |
| 54 | WHERE-shorthand form with W3C BGP restrictions — the CONSTRUCT WHERE { … } short form (template = WHERE pattern). |
The remaining Phase D slices (53 → cut) close out tests, docs, spec sync, and the release tag.
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 v3.7-staging graph."
SELECT * FROM pgrdf.construct(
'CONSTRUCT { ?s ?p ?o }
WHERE { GRAPH <http://example.org/v3.7-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 — Phase D close-out and the Phase E / v0.5 forward edge.