Skip to content

CONSTRUCT and DESCRIBE ​

CONSTRUCT builds new triples from a template. DESCRIBE returns the triples about a resource. Each form has its own function:

FunctionRuns
pgrdf.construct(q) → SETOF jsonbCONSTRUCT queries
pgrdf.describe(q) → SETOF jsonbDESCRIBE queries

pgrdf.sparql() refuses both forms with an error, so call these functions instead.

Examples use the sample data.

Row shape ​

Each triple is one row. Every term says what kind it is (iri, literal or bnode), and literals carry their datatype:

json
{"subject":   {"type": "iri", "value": "http://example.org/alice"},
 "predicate": {"type": "iri", "value": "http://xmlns.com/foaf/0.1/name"},
 "object":    {"type": "literal", "value": "Alice",
               "datatype": "http://www.w3.org/2001/XMLSchema#string"}}

A literal with a language tag also carries language:

json
{"type": "literal", "value": "cat", "language": "en",
 "datatype": "http://www.w3.org/1999/02/22-rdf-syntax-ns#langString"}

CONSTRUCT ​

Every foaf:Person becomes an ex:Agent, without changing the data:

sql
SELECT * FROM pgrdf.construct($$
  PREFIX foaf: <http://xmlns.com/foaf/0.1/>
  PREFIX ex:   <http://example.org/>
  CONSTRUCT { ?p a ex:Agent }
  WHERE     { ?p a foaf:Person }
$$);
--  {"object": {"type": "iri", "value": "http://example.org/Agent"},
--   "subject": {"type": "iri", "value": "http://example.org/carol"},
--   "predicate": {"type": "iri", "value": "http://www.w3.org/1999/02/22-rdf-syntax-ns#type"}}
--  … one row each for bob and alice

Templates can mix constants and variables:

sql
SELECT t->'object'->>'value' AS mentioned
  FROM pgrdf.construct($$
    PREFIX ex: <http://example.org/>
    CONSTRUCT { ex:report ex:mentions ?p }
    WHERE     { ?p ex:age ?a FILTER(?a > 30) }
  $$) AS t
 ORDER BY 1;
--         mentioned
-- --------------------------
--  http://example.org/alice
--  http://example.org/bob

Blank nodes in the template ​

A blank node in the template becomes a new blank node for each solution. Within one solution, the same label is the same node:

sql
SELECT * FROM pgrdf.construct($$
  PREFIX foaf: <http://xmlns.com/foaf/0.1/>
  PREFIX ex:   <http://example.org/>
  CONSTRUCT { ?p ex:contact _:c . _:c foaf:mbox ?m . }
  WHERE     { ?p foaf:mbox ?m }
$$);
--  {"object": {"type": "bnode", "value": "b1_1"}, "subject": {… "http://example.org/alice"}, "predicate": {… "http://example.org/contact"}}
--  {"object": {… "mailto:alice@example.org"},    "subject": {"type": "bnode", "value": "b1_1"}, …}
--  {"object": {"type": "bnode", "value": "b2_1"}, "subject": {… "http://example.org/carol"}, …}
--  {"object": {… "mailto:carol@example.org"},    "subject": {"type": "bnode", "value": "b2_1"}, …}

CONSTRUCT WHERE ​

When the template is the same as the pattern, write it once:

sql
SELECT * FROM pgrdf.construct($$
  PREFIX foaf: <http://xmlns.com/foaf/0.1/>
  CONSTRUCT WHERE { ?s foaf:name ?n }
$$);
--  three rows: alice, bob and carol with their foaf:name

One graph ​

Scope the pattern with GRAPH to copy a single graph out as triples:

sql
SELECT count(*) FROM pgrdf.construct($$
  CONSTRUCT { ?s ?p ?o }
  WHERE { GRAPH <http://example.org/people> { ?s ?p ?o } }
$$);
--  14

Use the rows as a table ​

sql
SELECT t->'subject'->>'value' AS subject,
       t->'object'->>'value'  AS object,
       t->'object'->>'type'   AS kind
  FROM pgrdf.construct($$
    PREFIX foaf: <http://xmlns.com/foaf/0.1/>
    CONSTRUCT WHERE { ?s foaf:name ?n }
  $$) AS t
 ORDER BY 1;
--          subject          | object |  kind
-- --------------------------+--------+---------
--  http://example.org/alice | Alice  | literal
--  http://example.org/bob   | Bob    | literal
--  http://example.org/carol | Carol  | literal

The same query works in a CREATE VIEW or CREATE TABLE … AS.

Keep the triples: INSERT … WHERE ​

CONSTRUCT only reads. To store the triples it would build, run the same template and pattern as a SPARQL UPDATE and name the target graph. The graph is created if it doesn't exist:

sql
SELECT * FROM pgrdf.sparql($$
  PREFIX foaf: <http://xmlns.com/foaf/0.1/>
  PREFIX ex:   <http://example.org/>
  INSERT { GRAPH <http://example.org/agents> { ?p a ex:Agent } }
  WHERE  { ?p a foaf:Person }
$$);
--  {"_update": {"form": "INSERT_WHERE", ..., "graphs_touched": ["http://example.org/agents"],
--               "triples_deleted": 0, "triples_inserted": 3}}

DESCRIBE ​

DESCRIBE returns the triples whose subject is the resource. When an object is a blank node, that node's triples are included too:

sql
SELECT d->'predicate'->>'value' AS predicate,
       d->'object'->>'value'    AS object
  FROM pgrdf.describe($$ DESCRIBE <http://example.org/bob> $$) AS d;
--                     predicate                    |              object
-- -------------------------------------------------+----------------------------------
--  http://xmlns.com/foaf/0.1/knows                 | http://example.org/carol
--  http://example.org/age                          | 41
--  http://www.w3.org/1999/02/22-rdf-syntax-ns#type | http://xmlns.com/foaf/0.1/Person
--  http://xmlns.com/foaf/0.1/name                  | Bob

Triples that point at the resource (Alice foaf:knows Bob) are not part of its description.

A pattern chooses the resources:

sql
SELECT * FROM pgrdf.describe($$
  PREFIX foaf: <http://xmlns.com/foaf/0.1/>
  DESCRIBE ?p WHERE { ?p foaf:name "Carol" }
$$);
--  four rows: carol's age, type, name and mailbox

Errors ​

  • pgrdf.construct() given something other than a CONSTRUCT query, a query that doesn't parse, or a template variable that the WHERE never binds is refused with SQLSTATE 22023.
  • pgrdf.describe() given something other than a DESCRIBE query is refused with an error.

See Errors and refusals.

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