How to Execute SPARQL Queries over RDF Graphs in Semantica: A Complete Backend Guide
Semantica provides three interchangeable backends—RDF4J, Oxigraph, and Jena—that implement a common execute_sparql API for querying RDF triplets with SPARQL.
The semantica-agi/semantica repository offers a flexible triplet store abstraction that lets you execute SPARQL queries against remote servers, embedded databases, or in-memory session graphs without changing your application code. All backends return results in a standardized JSON structure containing bindings, variables, triples, or metadata depending on the query type.
Choosing Your SPARQL Backend
Semantica's store backend registry (semantica/vector_store/registry.py) automatically instantiates the appropriate class based on your configuration. Here is how each backend compares:
- RDF4JStore — HTTP client for Eclipse RDF4J repositories; ideal for production deployments with existing RDF4J infrastructure.
- OxigraphStore — Embedded PyOxigraph store running in-process; best for local development or serverless deployments requiring zero external dependencies.
- JenaStore — Connector to Apache Jena/Fuseki endpoints with optional
rdflib.Datasetfallback; suitable for organizations standardized on Jena.
Changing backends requires only a configuration update. The same execute_sparql(query: str, **options) signature works identically across all three.
The Common execute_sparql API
Each backend implements execute_sparql in semantica/triplet_store/[backend]_store.py. The method follows a five-step pipeline:
-
Query type detection — A regex in
sparql_escaping.CONSTRUCT_QUERY_REdetermines whether the result should use"construct"or"bindings"format (RDF4JStore._is_construct_query). -
Result format validation — Only
"construct"or"bindings"are permitted; invalid values raiseValidationError(OxigraphStore.execute_sparql). -
Query execution — RDF4J uses
requests.postwith negotiatedAcceptheaders; Oxigraph runs native queries against the embedded store; Jena delegates tordflib.DatasetorSPARQLUpdateStore. -
Response parsing — CONSTRUCT results become Turtle-parsed graphs or
QueryTriplesiterators; SELECT/ASK results become variable binding lists. -
Progress tracking — All calls are wrapped by
ProgressTracker.start_tracking/stop_trackingfor observability and error handling.
The standardized return shape is:
{
"success": true,
"bindings": [],
"variables": [],
"triples": [],
"metadata": {"query": "...", "endpoint": "...", "result_format": "..."}
}
Executing SPARQL with RDF4JStore
The RDF4J backend (semantica/triplet_store/rdf4j_store.py) connects to external repositories over HTTP. It automatically sets Accept: application/sparql-results+json for SELECT queries and Accept: text/turtle for CONSTRUCT queries.
from semantica.triplet_store import RDF4JStore
store = RDF4JStore(
endpoint="http://localhost:8080/rdf4j-server",
repository_id="myrepo"
)
query = """
SELECT ?s ?p ?o
WHERE {
?s ?p ?o .
FILTER(?s = <http://example.org/subject>)
}
"""
result = store.execute_sparql(query)
print(result["bindings"])
The execute_sparql method auto-detects result_format="bindings" for this SELECT query. CONSTRUCT queries return serialized triples in result["triples"] after Turtle parsing (source).
Executing SPARQL with OxigraphStore
The Oxigraph backend (semantica/triplet_store/oxigraph_store.py) runs an embedded store without network dependencies. It supports both in-memory and on-disk persistence.
from semantica.triplet_store import OxigraphStore
store = OxigraphStore() # in-memory default
# Populate with data...
store.add_triplets([...])
construct_q = """
CONSTRUCT { ?s <http://example.org/hasLabel> ?label }
WHERE {
?s <http://example.org/name> ?label .
}
"""
result = store.execute_sparql(construct_q, result_format="construct")
print(result["triples"]) # [(s, p, o, metadata), ...]
Oxigraph handles QuerySolutions and QueryTriples iterators natively, normalizing them to the shared result format (source).
Executing SPARQL with JenaStore
The Jena backend (semantica/triplet_store/jena_store.py) supports remote Fuseki endpoints and named graphs.
from semantica.triplet_store import JenaStore
from semantica.semantic_extract.triplet_extractor import Triplet
store = JenaStore(endpoint="http://localhost:3030/ds")
triplets = [
Triplet(
subject="http://example.org/Alice",
predicate="http://example.org/knows",
object="http://example.org/Bob"
)
]
store.add_triplets(triplets, graph="http://example.org/graph1")
For queries, JenaStore uses either a remote SPARQLUpdateStore or an in-memory rdflib.Dataset, then normalizes results to the standard shape (source).
Using the Read-Only SPARQL HTTP Endpoint
Semantica's Explorer API exposes a safe, read-only SPARQL endpoint at /api/sparql (semantica/explorer/routes/sparql.py). This route:
- Validates queries with
_is_read_only_queryto blockINSERT,DELETE, and other update operations (source). - Materializes the current
GraphSessioninto an in-memoryrdflib.Graphcapped at 50,000 nodes/edges. - Truncates results to
_SPARQL_MAX_ROWS = 5,000to prevent response bloat.
curl -X POST http://localhost:8000/api/sparql \
-H "Content-Type: application/json" \
-d '{"query":"SELECT ?s WHERE { ?s a <http://semantica.local/entity/Person> } LIMIT 10"}'
Response format:
{
"columns": ["s"],
"rows": [{"s": "http://semantica.local/entity/JohnDoe"}],
"total": 1,
"truncated": false,
"error": null
}
This endpoint isideal for debugging and ad-hoc exploration without affecting persistent stores (route source).
Configuration-Driven Backend Selection
Switch backends via YAML configuration without code changes:
store_backend: "rdf4j"
rdf4j:
endpoint: "http://localhost:8080/rdf4j-server"
repository_id: "myrepo"
Valid store_backend values are "rdf4j", "oxigraph", or "jena". The registry instantiates the corresponding class from semantica/triplet_store/[backend]_store.py while preserving the identical execute_sparql interface.
Helper Utilities for SPARQL Query Handling
The sparql_escaping module (semantica/triplet_store/sparql_escaping.py) provides shared utilities:
CONSTRUCT_QUERY_RE— Regex for detecting CONSTRUCT/DESCRIBE queries.- URI validation and literal escaping for safe query construction.
These helpers ensure consistent behavior across all three backends when parsing and validating SPARQL syntax.
Summary
- Three interchangeable backends (RDF4J, Oxigraph, Jena) implement the same
execute_sparqlAPI insemantica/triplet_store/. - Standardized result format returns
bindings,variables,triples, andmetadataregardless of backend. - Auto-detection of query type via
sparql_escaping.CONSTRUCT_QUERY_REsets appropriate result handling. - Read-only HTTP endpoint at
/api/sparqlenables safe session exploration without backend configuration. - Configuration-driven selection lets you swap remote, embedded, or in-memory stores without code changes.
Frequently Asked Questions
How does Semantica determine whether a SPARQL query returns bindings or triples?
The _is_construct_query method in RDF4JStore uses a regex defined as sparql_escaping.CONSTRUCT_QUERY_RE to detect CONSTRUCT and DESCRIBE queries. When matched, the default result_format becomes "construct"; otherwise "bindings" is used for SELECT and ASK queries (source).
Can I execute SPARQL UPDATE queries through Semantica's backends?
Direct UPDATE support varies by backend. RDF4JStore and JenaStore can perform updates through their respective HTTP protocols, but the /api/sparql Explorer endpoint explicitly blocks all update operations via _is_read_only_query validation. For production updates, use the backend-specific methods rather than the read-only HTTP route (sanitization source).
What is the maximum result size for the Explorer SPARQL endpoint?
The endpoint truncates results to _SPARQL_MAX_ROWS = 5,000 rows and materializes in-memory graphs with a cap of 50,000 nodes/edges. These limits prevent memory exhaustion when querying large session graphs through the lightweight HTTP interface (graph building source).
Does OxigraphStore require an external server process?
No. OxigraphStore runs PyOxigraph embedded in the Python process with no external dependencies. It operates in-memory by default and can optionally persist to disk, making it suitable for serverless deployments and local development where managing separate database processes is undesirable (implementation).
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →