Skip to content

searchQuery ​

Read a graph with SPARQL 1.1. Query returns JSONB rows you can join straight back into regular SQL, with no bridge and no second protocol.

What it is ​

Query parses SPARQL, translates it to SQL over pgRDF's indexed triple storage, and runs it with a per-session plan cache. Variables shared across triple patterns become joins, and each solution comes back as one JSONB row.

How you run it ​

sparql — SELECT, ASK and UPDATE ​

sql
pgrdf.sparql(query TEXT) → SETOF JSONB

The main entry point. It runs SELECT and ASK, and executes SPARQL UPDATE inside your transaction, so ROLLBACK undoes it. See Pillar 2 — Semantic query.

  • Every value comes back as a string, numbers included ({"age": "34"}). Cast in SQL when you need a number.
  • An unbound variable comes back as JSON null.
  • ASK returns {"_ask": "true"} or {"_ask": "false"}.
  • UPDATE returns a report, for example {"_update": {"form": "INSERT_DATA", "triples_inserted": 1, ...}}.

construct — build triples ​

sql
pgrdf.construct(query TEXT) → SETOF JSONB

Runs a CONSTRUCT query and returns one row per triple, each term typed. See CONSTRUCT.

describe — describe a resource ​

sql
pgrdf.describe(query TEXT) → SETOF JSONB

Runs a DESCRIBE query and returns the resource's triples in the same shape as construct.

sparql_parse — inspect without running ​

sql
pgrdf.sparql_parse(query TEXT) → JSONB

Parses a query without running it. The unsupported_algebra key lists anything pgRDF can't run, so you can check a query before you send it. See sparql_parse.

What's supported ​

Multi-pattern joins, FILTER, OPTIONAL, UNION, MINUS, VALUES, BIND, subqueries, aggregates with GROUP BY / HAVING, type-aware ORDER BY, GRAPH, property paths, and every SPARQL UPDATE form.

Things to know:

  • A query without GRAPH searches every graph. Wrap patterns in GRAPH <iri> { … } to scope them. FROM and FROM NAMED are ignored, so use GRAPH instead. See the GRAPH clause.
  • Some constructs are refused with an error that names them: SERVICE, FILTER EXISTS / NOT EXISTS, sequence paths p1/p2 (write separate triple patterns), blank nodes in query patterns, and functions outside the supported list. See the error contract.

Example ​

sql
SELECT pgrdf.add_graph('http://example.org/people');
SELECT pgrdf.parse_turtle('
@prefix ex:   <http://example.com/> .
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
ex:alice foaf:name "Alice" ; foaf:age 34 ; foaf:mbox <mailto:alice@example.com> .
ex:bob   foaf:name "Bob"   ; foaf:age 27 .
', pgrdf.graph_id('http://example.org/people'));

SELECT * FROM pgrdf.sparql(
  'PREFIX foaf: <http://xmlns.com/foaf/0.1/>
   SELECT ?n ?age WHERE { ?p foaf:name ?n ; foaf:age ?age } ORDER BY ?age');
-- {"n": "Bob", "age": "27"}
-- {"n": "Alice", "age": "34"}

SELECT * FROM pgrdf.sparql(
  'PREFIX foaf: <http://xmlns.com/foaf/0.1/>
   ASK { ?p foaf:mbox ?m }');
-- {"_ask": "true"}

SELECT * FROM pgrdf.describe('DESCRIBE <http://example.com/bob>');
-- {"subject": {"type": "iri", "value": "http://example.com/bob"},
--  "predicate": {"type": "iri", "value": "http://xmlns.com/foaf/0.1/name"},
--  "object": {"type": "literal", "value": "Bob", "datatype": "http://www.w3.org/2001/XMLSchema#string"}}
-- {"subject": {"type": "iri", "value": "http://example.com/bob"},
--  "predicate": {"type": "iri", "value": "http://xmlns.com/foaf/0.1/age"},
--  "object": {"type": "literal", "value": "27", "datatype": "http://www.w3.org/2001/XMLSchema#integer"}}

SELECT pgrdf.sparql_parse('SELECT ?s WHERE { SERVICE <http://example.net/sparql> { ?s ?p ?o } }');
-- {"form": "SELECT", "variables": ["s"], "bgp_patterns": [], "bgp_pattern_count": 0,
--  "unsupported_algebra": ["Service (federation)"]}

Because the results are rows, SQL can filter and cast them:

sql
SELECT r ->> 'n' AS name, (r ->> 'age')::int AS age
FROM pgrdf.sparql(
  'PREFIX foaf: <http://xmlns.com/foaf/0.1/>
   SELECT ?n ?age WHERE { ?p foaf:name ?n ; foaf:age ?age }') AS r
WHERE (r ->> 'age')::int > 30;
--  name  | age
-- -------+-----
--  Alice |  34

Where it sits in a chain ​

Usually last. Query works on any loaded graph at any time. After Reason has run, queries see the inferred triples too.

Scaling class — per query

Each call is planned and run like any other SQL statement, using the indexes. Query isn't what decides whether you need to carve; Reason and Validate are.

See also ​

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