Skip to content

GRAPH — named-graph scoping

Two shapes — literal IRI and variable — both first-class. Both compose with OPTIONAL, UNION, MINUS.

Two forms

FormMeaning
GRAPH <iri> { … }Restrict the inner pattern to exactly the graph bound to <iri> in _pgrdf_graphs.
GRAPH ?g { … }Evaluate the inner pattern against every non-default graph; bind ?g to each matching graph's IRI.

Both compile to a WHERE _pgrdf_quads.graph_id = … predicate on top of the inner BGP's hexastore scan, so the SQL planner picks the right index and join order.

Why you'd use it

  • Project managers — multi-graph datasets are first-class: separate graphs per tenant, per snapshot, per ontology version. Querying them is one extra clause in the SPARQL, not a separate endpoint.
  • Data scientists — diff a live graph against a frozen snapshot in the same SPARQL call with two GRAPH clauses, or collect "which graphs contain this triple?" with GRAPH ?g.
  • Ontologists — keep imported vocabularies in their own graphs; reference each explicitly when combining instance and schema knowledge.
  • Backend engineers — graph identity is part of the query, not a connection-pool-level setting. Same SPARQL, different IRI, different scope.
  • Operators — partition routing is a planner-level predicate; per-graph queries hit only the partition they target.

Literal-IRI form

sql
-- Bind a graph IRI once.
SELECT pgrdf.add_graph(7, 'http://example.org/g1');

-- Then query that graph by IRI.
SELECT * FROM pgrdf.sparql(
  'SELECT ?s ?p ?o
     WHERE { GRAPH <http://example.org/g1> { ?s ?p ?o } }');

The IRI is resolved to a graph_id at translate time. An unknown IRI yields zero solutions (the partition doesn't exist).

Variable form

GRAPH ?g { … } projects the matching graph's IRI as a solution variable. Useful when you want to know which graph(s) answered the question.

sql
SELECT pgrdf.add_graph(1, 'http://example.org/g1');
SELECT pgrdf.add_graph(2, 'http://example.org/g2');
SELECT pgrdf.parse_turtle('@prefix ex: <http://e.com/>. ex:a ex:p "in g1".', 1);
SELECT pgrdf.parse_turtle('@prefix ex: <http://e.com/>. ex:a ex:p "in g2".', 2);

-- "Which graphs assert ex:a ex:p ?o, and what value did they give?"
SELECT * FROM pgrdf.sparql(
  'PREFIX ex: <http://e.com/>
   SELECT ?g ?o WHERE { GRAPH ?g { ex:a ex:p ?o } }');
--  → {"g": "http://example.org/g1", "o": "in g1"}
--  → {"g": "http://example.org/g2", "o": "in g2"}

Composition with OPTIONAL / UNION / MINUS

GRAPH clauses compose naturally with all the solution-combining operators. The inner BGP is scoped to the named graph; the outer combinator works exactly as it would against the default graph.

OPTIONAL inside GRAPH

sql
-- "For each named graph, every subject with foaf:name and
-- optionally a foaf:mbox."
SELECT * FROM pgrdf.sparql(
  'PREFIX foaf: <http://xmlns.com/foaf/0.1/>
   SELECT ?g ?s ?n ?m
     WHERE { GRAPH ?g {
               ?s foaf:name ?n
               OPTIONAL { ?s foaf:mbox ?m }
             } }');

UNION across two literal GRAPHs

sql
-- Combine two named graphs' contribution into one result.
SELECT * FROM pgrdf.sparql(
  'PREFIX ex: <http://e.com/>
   SELECT ?s ?o
     WHERE { { GRAPH <http://example.org/g1> { ?s ex:p ?o } }
             UNION
             { GRAPH <http://example.org/g2> { ?s ex:p ?o } } }');

MINUS to subtract a snapshot

sql
-- "Subjects present in production but not in last week's snapshot."
SELECT * FROM pgrdf.sparql(
  'SELECT ?s
     WHERE { GRAPH <http://example.org/prod>     { ?s a <http://e.com/T> }
             MINUS
             { GRAPH <http://example.org/snap-w34> { ?s a <http://e.com/T> } } }');

Tests

See also

Apache-2.0 licensed. Documentation for pgRDF — built with VitePress, served via GitHub Pages.