Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

SPARQL Engine

Implementation status (2026-07-23, kelly): ✅ Implemented (code is AHEAD of this doc). SELECT/ASK/CONSTRUCT/DESCRIBE, BGP/JOIN/UNION/OPTIONAL/FILTER/BIND, DISTINCT/ORDER/LIMIT/GROUP/HAVING, aggregates (COUNT/SUM/AVG/MIN/MAX/SAMPLE/ GROUP_CONCAT), FILTER builtins, RDFS subclass inference, and bitemporal current-state filtering are all live (src/sparql/). Note the doc UNDER-claims: it marks “Property paths: Planned,” but they are fully implemented in src/sparql/property_path.rs (Reverse/Sequence/Alternative/ZeroOrMore/OneOrMore) — the table row below is now corrected.

Quipu includes a custom SPARQL evaluator that compiles queries directly against the SQLite fact log. No separate triple store or graph database is needed.

How It Works

  1. Parse: SPARQL string -> AST via spargebra
  2. Evaluate: Walk the AST, executing each graph pattern against SQLite
  3. Return: Variable bindings as HashMap<String, Value> rows
#![allow(unused)]
fn main() {
use quipu::store::Store;
use quipu::sparql;

let result = sparql::query(&store,
    "SELECT ?name WHERE { ?s <http://example.org/name> ?name }"
).unwrap();

for row in result.rows() {
    println!("{:?}", row.get("name"));
}
}

Query Forms

FormDescriptionExample
SELECTReturn variable bindingsSELECT ?s ?p ?o WHERE { ... }
ASKBoolean existence checkASK { ?s a ex:Person }
CONSTRUCTBuild new triplesCONSTRUCT { ?s a ex:Result } WHERE { ... }
DESCRIBEReturn all facts about an entityDESCRIBE <http://example.org/alice>

Supported Features

Graph Patterns

PatternStatusExample
Basic Graph Pattern (BGP)Supported?s ?p ?o
JOINSupportedMultiple BGP patterns
UNIONSupported{ ... } UNION { ... }
FILTERSupportedFILTER(?age > 30)
OPTIONAL (LeftJoin)SupportedOPTIONAL { ?s ex:email ?e }
PROJECTSupportedSELECT ?name
DISTINCT / REDUCEDSupportedSELECT DISTINCT ?type
LIMIT / OFFSETSupportedLIMIT 10 OFFSET 5
ORDER BYSupportedORDER BY DESC(?age)
GROUP BYSupportedGROUP BY ?type
HAVINGSupportedHAVING(COUNT(?s) > 2)
EXTEND (BIND)SupportedComputed variables
VALUESSupportedVALUES ?x { "a" "b" }, multi-column, UNDEF (src/sparql/values.rs)
Property pathsSupportedReverse ^, Sequence /, Alternative |, *, + (src/sparql/property_path.rs)

Aggregates

FunctionExample
COUNTSELECT (COUNT(?s) AS ?n) WHERE { ... }
SUMSELECT (SUM(?age) AS ?total) ...
AVGSELECT (AVG(?age) AS ?mean) ...
MIN / MAXSELECT (MIN(?age) AS ?youngest) ...

FILTER Expressions

ExpressionExample
Equality?name = "Alice"
Comparison?age > 30, ?age <= 50
AND / OR / NOT?age > 20 && ?age < 40
BOUNDBOUND(?name)
Regexregex(?name, "Ali")
CONTAINSCONTAINS(STR(?s), "traefik")
LCASE / STRLCASE(STR(?name))
isIRIFILTER(isIRI(?o))
IN / NOT INFILTER(?name IN ("Alice", "Bob"))

RDFS Inference

Quipu supports RDFS subclass inference for rdf:type queries. If you define:

ex:Engineer rdfs:subClassOf ex:Person .
ex:alice a ex:Engineer .

Then SELECT ?s WHERE { ?s a ex:Person } will return ex:alice through transitive subclass reasoning.

Temporal Awareness

The SPARQL engine automatically filters to current state (op = 1 AND valid_to IS NULL). Time-travel is supported via the unravel command:

# See the world as it was at a specific transaction
quipu unravel --tx 5 --db my.db

# See the world as it was at a specific time
quipu unravel --valid-at "2026-03-15T00:00:00Z" --db my.db

Via the MCP tool:

{
  "tool": "quipu_query",
  "input": {
    "query": "SELECT ?s ?p ?o WHERE { ?s ?p ?o }",
    "valid_at": "2026-03-15T00:00:00Z"
  }
}