Apache Jena backend (SPARQL)
cvcdocdb.jena_graph stores graphs as RDF 1.2 in Apache Jena Fuseki (or
another SPARQL 1.2 store, see below), with the same API and behaviour as every other
backend. cvcdocdb.jena_rdf defines the RDF layout, so the data can also
be queried directly with SPARQL.
from cvcdocdb import JenaGraph, Node
graph = JenaGraph("http://localhost:3030/ds") # a Fuseki dataset
graph.insertNode(Node(pk={"doc": "D1"}, main_label="Document", title="Padró"))
graph.query(
"PREFIX label: <urn:cvcdocdb:label/> PREFIX prop: <urn:cvcdocdb:prop/> "
"SELECT ?t WHERE { ?d a label:Document ; prop:title ?t }"
) # [{'t': 'Padró'}]
How it works:
It reuses the
NetworkXGraphlogic (identical toNeo4jGraph’s: the shared propagation scenarios check it) on an in-memory copy of the graph, so the graph has to fit in RAM.Every mutating call, or a whole
batch(), is sent as one atomic SPARQL Update with only what changed. If it fails, nothing is sent and the copy is rolled back.A version stamp detects writes from other clients. The copy is refreshed before each write. If another client writes in the middle of a call or batch, nothing is applied and
ConcurrentModificationErroris raised.query()runs SPARQL strings on the server (SPARQL Update is refused: writes go through the API so the propagation policy applies). Dict filters and Cypher run on the in-memory copy, as onNetworkXGraph.namespace(defaulturn:cvcdocdb:) prefixes every IRI it writes. Triples outside it are never touched, andgraph_iristores the data in a named graph.clear()deletes everything in the namespace.Vector indexes are not supported (as on Neo4j).
Requirement: Apache Jena Fuseki >= 6.2.0 (RDF 1.2 triple terms and
annotations). JenaGraph reads the version from Fuseki’s /$/server
endpoint on connection, before reading any data, and raises
FusekiVersionError if it is older or can’t be
determined. Pass server_url= if Fuseki is behind a proxy, or
check_fuseki_version=False to use another SPARQL 1.2 store at your own
risk. The version is available as graph.server_version.
Natural-language questions: Natural-language queries in SPARQL (Text2SPARQL) or Natural-language queries on any backend (Text2Query).
JenaGraph — Apache Jena (SPARQL) backend for the DRM graph model.
JenaGraph stores the graph as RDF 1.2 in a SPARQL store such as
Apache Jena Fuseki (see
cvcdocdb.jena_rdf for the RDF layout), with the same API and the same
behaviour as every other backend.
How it works:
It reuses
NetworkXGraph’s logic, which behaves exactly likeNeo4jGraph(the shared propagation scenarios check it), on an in-memory copy of the graph.Every mutating call, or a whole
batch(), is sent to the server as a single atomic SPARQL Update request with only what changed. If the call fails, nothing is sent and the in-memory copy is rolled back.Writes are protected by a version stamp. Before each write, the copy is refreshed if another client changed the graph. If another client writes in the middle of a call or batch, nothing is applied, the copy is reloaded, and
ConcurrentModificationErroris raised.query()with a SPARQL string runs it on the server. Dict filters and Cypher strings run on the in-memory copy, as onNetworkXGraph.
The whole graph is held in memory, so the graph has to fit in RAM.
- exception cvcdocdb.jena_graph.ConcurrentModificationError[source]
Bases:
RuntimeErrorAnother client changed the graph during this call or batch; nothing was applied and the in-memory copy has been reloaded. Retry the call.
- exception cvcdocdb.jena_graph.FusekiVersionError[source]
Bases:
SparqlErrorThe server isn’t Apache Jena Fuseki >=
MIN_FUSEKI_VERSION, or its version can’t be determined.
- class cvcdocdb.jena_graph.JenaGraph(url, user=None, password=None, *, namespace='urn:cvcdocdb:', graph_iri=None, query_url=None, update_url=None, timeout=30.0, lock_timeout=None, server_url=None, check_fuseki_version=True)[source]
Bases:
NetworkXGraphGraph store on Apache Jena Fuseki (or another SPARQL 1.2 store).
- Parameters:
url (str) – Dataset URL, e.g.
http://localhost:3030/ds. Queries go to<url>/queryand updates to<url>/updateunless query_url/update_url are given.user (Optional[str]) – Optional HTTP basic-auth user.
password (Optional[str]) – Optional HTTP basic-auth password.
namespace (str) – IRI prefix of everything this graph writes (default
urn:cvcdocdb:). Triples outside it are never touched, so several graphs can share a dataset with different namespaces.graph_iri (Optional[str]) – Store the data in this named graph instead of the default graph.
query_url (Optional[str]) – Explicit SPARQL query endpoint.
update_url (Optional[str]) – Explicit SPARQL update endpoint.
timeout (float) – Seconds to wait for each HTTP request.
lock_timeout (Optional[float]) – Seconds a mutating call waits for another thread of this process using the same instance (
None: forever).server_url (Optional[str]) – Fuseki’s base URL, if it can’t be derived from url (e.g. behind a proxy); its
/$/serverendpoint reports the version.check_fuseki_version (bool) – Require Apache Jena Fuseki >=
MIN_FUSEKI_VERSION(default). Set toFalseto use another SPARQL 1.2 store, at your own risk.
- server_version
The Fuseki version reported by the server, or
Noneif it couldn’t be determined (only possible withcheck_fuseki_version=False).
- Raises:
FusekiVersionError – If the server isn’t Fuseki >= 6.2.0, or its version can’t be determined (and check_fuseki_version is on). Raised before any data is read.
SparqlError – If the server can’t be reached or rejects a request.
- Parameters:
- __init__(url, user=None, password=None, *, namespace='urn:cvcdocdb:', graph_iri=None, query_url=None, update_url=None, timeout=30.0, lock_timeout=None, server_url=None, check_fuseki_version=True)[source]
- Parameters:
persistence_path – Pickle file backing the graph; defaults to a per-workspace file under
~/.cache/cvcdocdb.lock_timeout (float | None) – Maximum seconds a mutating call (or
batch(), includingcvcdocdb.migration.migrate()) waits for the cross-process file lock before raisingGraphLockTimeout.None(default) waits forever, as before.url (str)
user (str | None)
password (str | None)
namespace (str)
graph_iri (str | None)
query_url (str | None)
update_url (str | None)
timeout (float)
server_url (str | None)
check_fuseki_version (bool)
- Raises:
ValueError – If lock_timeout is negative.
- Return type:
None
- query(filter_dict=None, projection=None, sort=None, limit_val=None, params=None)[source]
Like
NetworkXGraph.query(), plus SPARQL.A SPARQL string (
SELECT/ASK/CONSTRUCT/DESCRIBE, with an optionalPREFIX/BASEprologue) runs on the server and sees the committed data. params are bound with a trailingVALUESclause ({"name": value}binds?name). Rows map variables to Python values (IRIs as strings).ASKreturns[{"ask": bool}];CONSTRUCT/DESCRIBEreturn one{"subject", "predicate", "object"}row per triple.SPARQL Update is refused (
ValueError): writes go through theGraphStoreAPI so the propagation policy is applied. Dict filters and Cypher run on the in-memory copy, as on NetworkXGraph.
- clear()[source]
Delete everything this graph wrote (its namespace) on the server. Triples outside the namespace are left alone.
- Return type:
None
- close()[source]
Release resources and clear the graph.
Does NOT persist anything itself — every mutating method (
insertNode/insertRelation/deleteNode/init_propagation/create_group/enable_vector_index) already reloads the latest on-disk state and saves as part of a cross-process file-locked load-mutate-save cycle (see_guarded_write), so by the timeclose()runs the on-disk state already reflects every change made through this instance — including any other process’s writes that happened in between.This used to call
_save_state()unconditionally, which re-wrote the entire file with whatever was loaded into memory when this instance was opened — including for an instance that only ever did reads. Two overlapping opens of the same persistence path (e.g. a slow read in one process/request and a write in another) would race: whichever instance closed last silently overwrote the other’s writes with its own (possibly stale) snapshot, even though it never mutated anything itself. Removing the call here means a read-only instance can never clobber someone else’s concurrent write on close. The remaining “two writers” race (both mutate, neither just reads) is handled separately by_guarded_write’s reload-before-mutate under a cross-process lock.- Return type:
None
- cvcdocdb.jena_graph.MIN_FUSEKI_VERSION = (6, 2, 0)
Oldest Apache Jena Fuseki release JenaGraph supports (RDF 1.2 triple terms and annotations, SPARQL 1.2 results; the version CI tests against).
- exception cvcdocdb.jena_graph.SparqlError[source]
Bases:
RuntimeErrorThe SPARQL server rejected a request or could not be reached.
- cvcdocdb.jena_graph.is_sparql(text)[source]
Whether text is SPARQL (query or update) rather than Cypher.
RDF layout
Mapping between the state of a cvcdocdb graph and RDF 1.2 triples.
Used by JenaGraph to store, in a SPARQL store
such as Apache Jena Fuseki, exactly the state NetworkXGraph keeps in
memory. The RDF is meant to be queried directly with SPARQL. With the
default namespace urn:cvcdocdb::
a node is
<urn:cvcdocdb:node/ID>; it isa <urn:cvcdocdb:label/L>for each of its labels, and each propertynameis a triple<urn:cvcdocdb:prop/name> value;a relation is the triple
<node/U> <urn:cvcdocdb:rel/TYPE> <node/V>. Its properties hang from a reifier<urn:cvcdocdb:edge/U/V/TYPE>withrdf:reifies <<( <node/U> <rel/TYPE> <node/V> )>>(RDF 1.2);bookkeeping lives under
<urn:cvcdocdb:meta/...>: each node’s main label and pk, the label order, and the id counter.
Values: str → plain literal, bool → xsd:boolean, int →
xsd:integer, float → xsd:double, date/datetime →
xsd:date/xsd:dateTime, and anything else (lists, dicts, None) →
an rdf:JSON literal. Names (labels, properties, relation types) are
percent-encoded into the IRIs.
Terms are handled in two forms: canonical N-Triples-like strings (what is
sent to the server, and what triples are compared by) and parsed tuples
(("uri", iri), ("literal", lexical, datatype, lang),
("triple", s, p, o)).
- cvcdocdb.jena_rdf.parse_term(text)[source]
Parse a canonical term string (see
term_to_string()).
- cvcdocdb.jena_rdf.term_from_binding(binding)[source]
Parsed term from a SPARQL 1.2 JSON results binding.
- class cvcdocdb.jena_rdf.RdfMapping(namespace='urn:cvcdocdb:')[source]
IRIs and value encoding for one namespace (see the module docstring).
- Parameters:
namespace (str)
- state_to_triples(node_attrs, edge_attrs, meta)[source]
Every triple that represents node_attrs/edge_attrs/meta.
- state_from_triples(triples)[source]
Inverse of
state_to_triples(). Triples outside the namespace (or not written by cvcdocdb) are ignored.