SPARQL

Use SPARQL for data represented as subjects, predicates, and objects. The read interface supports SELECT, ASK, CONSTRUCT, and bounded DESCRIBE, together with the query forms below.

Send a query

In a web console, select your database and choose SPARQL in Direct Query. From a shell, first set up the downloadable API definitions, then run:

grpcurl -plaintext \
  -import-path "$SHRIKEDB_API_DIR" -proto shrikedb.v2.proto \
  -d '{"query":"SELECT ?s ?p ?o WHERE { ?s ?p ?o } LIMIT 10"}' \
  127.0.0.1:50251 shrikedb.v2.ShrikeDbService/ExecuteSPARQL

The response streams typed result rows. An empty database returns no matching rows. Add authentication and database-selection headers as shown in Connect To ShrikeDB.

Inspect predicates before writing a pattern

SELECT DISTINCT ?p WHERE { ?s ?p ?o }
ORDER BY ?p
LIMIT 25

Use the exact predicate IRIs present in the loaded data. Prefix declarations shorten IRIs; they do not rename stored predicates. The examples below use http://example.org/ as a sample namespace and expect matching data unless they use only VALUES.

Optional data and filters

This query keeps each resource even if its label is absent:

PREFIX ex: <http://example.org/>
SELECT ?s ?label WHERE {
  ?s ex:type ex:Thing .
  OPTIONAL { ?s ex:label ?label }
}
LIMIT 20

OPTIONAL preserves the left-side row when the right pattern has no match. A filter inside the optional block controls which optional values attach; a filter outside the block can remove the entire row. Correlated optional filters can reference variables supplied by the left side and the optional pattern. Out-of-scope variables are rejected.

MINUS excludes compatible matching bindings. Disjoint-variable MINUS leaves the left input unchanged. FILTER EXISTS and FILTER NOT EXISTS test whether a pattern has matches, including supported correlated patterns.

Inline values, expressions, and aggregates

This example needs no stored triples. It returns a count of three and a sum of six:

SELECT (COUNT(?n) AS ?items) (SUM(?n) AS ?total) WHERE {
  VALUES ?n { 1 2 3 }
}

VALUES creates bindings; UNDEF represents an unbound value. BIND adds a computed binding. Supported aggregate functions include COUNT, SUM, MIN, MAX, AVG, GROUP_CONCAT, and SAMPLE. GROUP BY forms groups and HAVING filters the aggregate results.

Use ORDER BY when page order matters, then apply OFFSET and LIMIT. DISTINCT removes duplicate projected rows.

Derive a result graph

Use CONSTRUCT to express relationships calculated by a query. It returns triples without inserting them into the database. For example, these inline account pairs produce one derived relationship even though the input contains the same pair twice:

CONSTRUCT { ?from <urn:analysis:relatedTo> ?to }
WHERE {
  VALUES (?from ?to) {
    (<urn:account:a> <urn:account:b>)
    (<urn:account:a> <urn:account:b>)
  }
}

The row API returns subject, predicate, and object. Each value retains its RDF kind and, for literals, its datatype or language. The gRPC API encodes these RDF term objects as JSON in bytes_val. This example returns the triple <urn:account:a> <urn:analysis:relatedTo> <urn:account:b>; querying the stored graph afterward does not find that triple unless it already existed.

Replace VALUES with supported graph patterns, joins, filters, aggregates or local SpaceKey searches to derive relationships from your data. A template blank node is fresh for each input solution; repeated uses of the same blank-node label within that solution refer to the same result node. Duplicate triples are removed. Unbound template variables and invalid subject or predicate positions omit the affected triple.

Default queries allow up to 10,000 source triples per scan and 10,000 distinct result triples. Construction also enforces a 64 MiB result-size budget, including its per-term accounting allowance. Exceeding a limit returns an error; restrict source patterns or time windows to reduce the work. Use variables in WHERE patterns to match unnamed nodes. RDF-star result terms are unsupported. Persisting selected results requires a separate write through the graph APIs or Cypher mutations described below.

Property paths

Sequence, alternative, inverse, and repetition paths are available. For example:

PREFIX ex: <http://example.org/>
SELECT ?person ?ancestor WHERE {
  ?person ex:parent+ ?ancestor .
}
LIMIT 20

+ follows one or more edges, * includes zero hops, and ? permits zero or one. Traversal uses visited sets and resource caps for cyclic graphs. Large expansions can hit an explicit limit; reducing returned rows alone may not reduce the traversal required.

Named graphs and descriptions

GRAPH, FROM, and FROM NAMED use the dataset’s graph-membership metadata. Ensure the imported data includes the graph membership you intend to query.

DESCRIBE returns a bounded, deterministic outgoing direct context for the described resource. Use explicit graph patterns when you need a particular traversal or a larger neighborhood.

Expression behavior

Typed literals and language tags are preserved for supported expressions. Term functions include STR, LANG, DATATYPE, IRI, BNODE, STRLANG, STRDT, type predicates, and sameTerm. String, numeric, REGEX, and REPLACE operations validate their operands and supported flags.

IF evaluates the selected branch; COALESCE skips null or expression-error candidates. Supported conditional EXISTS forms can be used in FILTER and BIND. Conditional existence inside an optional filter is evaluated against optional-side candidates; left-only correlation in that form is rejected. General combinations of existence expressions, including some disjunctions and negations, remain restricted. Simplify a rejected expression into supported patterns while checking that the rewrite preserves its null behavior.

Weighted Path SERVICE Extensions

ShrikeDB supports SPARQL service lowering for weighted path procedures. The service block lowers to the same logical operator used by equivalent Cypher CALL gds…​ queries.

PREFIX shrike: <urn:shrikedb:>

SELECT ?target ?totalCost ?nodeIds WHERE {
  SERVICE <urn:shrikedb:gds.shortestPath.dijkstra.stream> {
    [] shrike:sourceNode "uid:1" ;
       shrike:targetNode "uid:42" ;
       shrike:relationshipType "ROAD" ;
       shrike:weightProperty "cost" ;
       shrike:targetNodeId ?target ;
       shrike:totalCost ?totalCost ;
       shrike:nodeIds ?nodeIds .
  }
}

A* adds coordinate properties:

PREFIX shrike: <urn:shrikedb:>

SELECT ?target ?totalCost WHERE {
  SERVICE <urn:shrikedb:gds.shortestPath.astar.stream> {
    [] shrike:sourceNode "uid:1" ;
       shrike:targetNode "uid:42" ;
       shrike:relationshipType "ROAD" ;
       shrike:weightProperty "cost" ;
       shrike:latitudeProperty "lat" ;
       shrike:longitudeProperty "lon" ;
       shrike:targetNodeId ?target ;
       shrike:totalCost ?totalCost .
  }
}

Yen k-shortest paths:

PREFIX shrike: <urn:shrikedb:>

SELECT ?index ?totalCost ?nodeIds WHERE {
  SERVICE <urn:shrikedb:gds.shortestPath.yens.stream> {
    [] shrike:sourceNode "uid:1" ;
       shrike:targetNode "uid:42" ;
       shrike:relationshipType "ROAD" ;
       shrike:weightProperty "cost" ;
       shrike:k 5 ;
       shrike:index ?index ;
       shrike:totalCost ?totalCost ;
       shrike:nodeIds ?nodeIds .
  }
}
ORDER BY ?index

Insert and delete triples

On a standalone server, set SHRIKEDB_SPARQL_UPDATE_MODE=canary in the server environment and restart it to enable the ExecuteSPARQLUpdate gRPC method. The setting defaults to off. The method accepts INSERT DATA and DELETE DATA operations in the default RDF graph, including a sequence that mixes the two. All operations in one request commit as one ordered block. The selected tenant needs write permission. Each successful call returns a committed receipt and the number of submitted triples.

grpcurl -plaintext \
  -import-path "$SHRIKEDB_API_DIR" -proto shrikedb.v2.proto \
  -d '{"update":"INSERT DATA { <urn:example:vessel:42> <urn:example:status> \"at sea\" . }"}' \
  127.0.0.1:50251 shrikedb.v2.ShrikeDbService/ExecuteSPARQLUpdate

Read the inserted triple with ExecuteSPARQL:

SELECT ?status WHERE {
  <urn:example:vessel:42> <urn:example:status> ?status
}

Delete that exact triple with the same update method:

grpcurl -plaintext \
  -import-path "$SHRIKEDB_API_DIR" -proto shrikedb.v2.proto \
  -d '{"update":"DELETE DATA { <urn:example:vessel:42> <urn:example:status> \"at sea\" . }"}' \
  127.0.0.1:50251 shrikedb.v2.ShrikeDbService/ExecuteSPARQLUpdate

You can submit several inserts together by separating operations with a semicolon:

INSERT DATA { <urn:example:vessel:42> <urn:example:status> "at sea" };
INSERT DATA { <urn:example:vessel:43> <urn:example:status> "at sea" }

The same form works for several DELETE DATA operations or a mix of inserts and deletes. Operations run in their written order: an insert followed by a delete of the same triple leaves it absent, while a delete followed by an insert leaves it present. DELETE DATA matches the complete RDF term. A different language tag, datatype, or IRI/literal kind does not remove the stored triple. Deleting an absent triple succeeds without changing the graph.

The update method accepts a request of at most 4 MiB. It rejects named-graph writes and updates on an HA server before writing. An unsupported operation anywhere in a request rejects the whole request. DELETE DATA also rejects uid: and idx: subjects, whose compact property-graph projections require a separate removal path. Use the graph write APIs for other supported ingestion paths. Set the tenant and authorization headers as described in Connect To ShrikeDB.

Local services and writes

The weighted-path examples require the named nodes and numeric ROAD weight properties to exist. A* additionally uses the supplied latitude/longitude fields. See Functions And Procedures for weight restrictions and Geospatial and spatiotemporal indexing for geographic observation searches.

ShrikeDB SERVICE extensions execute locally. Remote SERVICE requests are rejected by policy.

ExecuteSPARQL serves read queries. Send the supported mutation above to ExecuteSPARQLUpdate. For larger imports or other data models, use ontology/dataset imports, graph write APIs, or supported Cypher mutations.