Skip to content

searchQuerying with SPARQL ​

pgrdf.sparql(query) runs a SPARQL 1.1 query against the graphs in your database and returns one JSONB row per solution, in a column named sparql.

sql
SELECT * FROM pgrdf.sparql($$
  PREFIX foaf: <http://xmlns.com/foaf/0.1/>
  PREFIX ex:   <http://example.org/>
  SELECT ?name ?age
  WHERE { ?p foaf:name ?name ; ex:age ?age
          FILTER(?age >= 30) }
  ORDER BY ?name
$$);
--  {"age": "34", "name": "Alice"}
--  {"age": "41", "name": "Bob"}

The same function runs SPARQL UPDATE inside your transaction. CONSTRUCT and DESCRIBE have their own functions, pgrdf.construct() and pgrdf.describe().

Sample data ​

The examples in this section use this small graph. Create it once:

sql
SELECT pgrdf.add_graph('http://example.org/people');
SELECT pgrdf.parse_turtle($$
@prefix ex:   <http://example.org/> .
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
ex:alice a foaf:Person ; foaf:name "Alice" ; ex:age 34 ;
         foaf:mbox <mailto:alice@example.org> ;
         foaf:knows ex:bob , ex:carol .
ex:bob   a foaf:Person ; foaf:name "Bob" ; ex:age 41 ;
         foaf:knows ex:carol .
ex:carol a foaf:Person ; foaf:name "Carol" ; ex:age 29 ;
         foaf:mbox <mailto:carol@example.org> .
$$, pgrdf.graph_id('http://example.org/people'));
--  parse_turtle
-- --------------
--            14

The outputs shown assume this is the only data in the database. Pages that need more data create it in its own graph and say how to remove it.

Reading the results ​

  • Each variable becomes a key. A variable with no value is null.
  • Every value is a string, numbers included ("age": "34"). IRIs come through as their plain text.
  • An ASK query returns one row, {"_ask": "true"} or {"_ask": "false"}.
  • An UPDATE returns one {"_update": {…}} summary row.
  • A query without a GRAPH clause matches across all graphs.

Because results are rows, the SQL around the call can pick them apart, cast them and join them to your tables:

sql
SELECT sparql->>'name'             AS name,
       (sparql->>'age')::int + 1  AS next_year
  FROM pgrdf.sparql($$
    PREFIX foaf: <http://xmlns.com/foaf/0.1/>
    PREFIX ex:   <http://example.org/>
    SELECT ?name ?age WHERE { ?p foaf:name ?name ; ex:age ?age }
    ORDER BY ?name
  $$);
--  name  | next_year
-- -------+-----------
--  Alice |        35
--  Bob   |        42
--  Carol |        30

Reading data ​

  • search BGP joins: triple patterns that share variables.
  • search FILTER: comparisons, tests and string functions.
  • hub OPTIONAL: keep a solution when part of the pattern is missing.
  • hub UNION: alternative patterns.
  • hub MINUS: remove solutions that match a pattern.
  • query_stats Aggregates: COUNT, SUM, AVG, MIN, MAX, GROUP_CONCAT, SAMPLE with GROUP BY.
  • query_stats HAVING: filter groups.
  • search BIND and VALUES: computed values and inline lists of values.
  • search Solution modifiers: DISTINCT, ORDER BY, LIMIT, OFFSET, subqueries.
  • search ASK: yes-or-no queries.
  • account_tree GRAPH: query one named graph, or find which graphs match.
  • hub Property paths: ^, +, *, ? and |.

Changing data and building triples ​

  • code SPARQL UPDATE: INSERT DATA, DELETE DATA, INSERT … WHERE, DELETE … WHERE, DELETE … INSERT … WHERE, WITH, and CREATE / CLEAR / DROP GRAPH.
  • code CONSTRUCT and DESCRIBE: triples as rows, from a template or about a resource.

When something goes wrong ​

Not supported ​

pgRDF refuses these with an error that names the construct, so a query never silently returns a wrong answer because of them. What the codes mean: Errors and refusals.

ConstructRefused withInstead
SERVICE (federated query)0A000Fetch the remote data with your own client, load it into a graph, then query it.
FILTER EXISTS, FILTER NOT EXISTS0A000MINUS { … }, or OPTIONAL { … } with FILTER(!BOUND(?v)).
LANGMATCHES0A000LANG(?x) = "fr", or STRSTARTS(LANG(?x), "fr") to include tags like fr-CA.
COALESCE, SUBSTR and other functions not listed under FILTER or BIND0A000 in FILTER, XX000 in BINDDo it in SQL on the result rows: coalesce(), left(), substring(), casts.
Blank nodes in a query pattern (_:b, [])XX000Use a variable.
Sequence paths ?s p1/p2 ?oXX000Two patterns: ?s p1 ?mid . ?mid p2 ?o.
Negated property sets !(p); nested paths such as (p1/p2)+XX000?s ?p ?o FILTER(?p != …); separate patterns.
UNION inside OPTIONALXX000One OPTIONAL per alternative, combined in SQL.
UNION or FILTER inside MINUS0A000One MINUS block per alternative.
BIND inside a UNION branch0A000Compute the value in SQL on the result rows.
VALUES binding the variable of GRAPH ?gXX000List the graphs as GRAPH <iri> blocks joined with UNION, or filter the rows in SQL.
FILTER on the variable of GRAPH ?g0A000As above.
ORDER BY an expression together with DISTINCTXX000BIND the sort key, then ORDER BY the variable.
LOAD <url>0A000pgrdf.load_turtle() or pgrdf.parse_turtle().
RDF-star quoted triples (<< … >>)XX000Not available.

FROM and FROM NAMED are ignored

FROM <iri> and FROM NAMED <iri> are accepted but currently have no effect: the query still runs over all graphs. To choose graphs, use GRAPH <iri> { … }.

One more thing works differently without raising an error: a VALUES variable that no triple pattern uses comes back null for values that don't already occur in the database (see VALUES).

Learn more ​

Next: BGP joins →

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