RDF/Turtle Integration
GrafitoDB can export to and import from RDF (Resource Description Framework),
using rdflib under the hood. This makes a Grafito
property graph interoperable with the Semantic Web / Linked Data ecosystem.
Prerequisites
This installs rdflib for RDF handling.
Public API
The integration lives in grafito.integrations:
| Function | Purpose |
|---|---|
export_rdf(db, ...) |
Export the graph to an rdflib.Graph |
export_turtle(db, ...) |
Export the graph to a Turtle string |
export_string(db, format=..., ...) |
Export to any rdflib format as a string |
export_to_file(db, path, ...) |
Export to a file, format inferred from extension |
import_rdf(db, graph, ...) |
Import an rdflib.Graph into the database |
import_turtle(db, source, ...) |
Parse Turtle (string or file) and import it |
import_from_file(db, path, ...) |
Import a file, format inferred from extension |
query_sparql(db, query, ...) |
Run SPARQL over the graph (delegated to rdflib) |
graph_diff(a, b, ...) |
Compare two graphs by RDF isomorphism |
Exporting to RDF
Basic Export
from grafito import GrafitoDatabase
from grafito.integrations import export_rdf, export_turtle
db = GrafitoDatabase(':memory:')
alice = db.create_node(labels=['Person'], properties={'name': 'Alice'})
bob = db.create_node(labels=['Person'], properties={'name': 'Bob'})
db.create_relationship(alice.id, bob.id, 'KNOWS', {'since': 2021})
# Export to an rdflib Graph
rdf_graph = export_rdf(db, base_uri='http://example.org/')
print(f'Triples: {len(rdf_graph)}')
export_rdf signature:
export_rdf(
db,
base_uri="grafito:", # namespace for nodes/labels/property predicates
node_prefix="node/", # URI segment for nodes without an explicit uri
rel_prefix="rel/", # URI segment for reified relationships
prefixes=None, # dict {prefix: namespace} merged with the defaults
) -> rdflib.Graph
The following prefixes are always bound: rdf, rdfs, xsd, owl, schema.
Export to Turtle
turtle_str = export_turtle(
db,
base_uri='http://example.org/',
prefixes={
'schema': 'http://schema.org/',
'foaf': 'http://xmlns.com/foaf/0.1/',
},
)
with open('export.ttl', 'w') as f:
f.write(turtle_str)
Output:
@prefix : <http://example.org/> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
<http://example.org/rel/1> a :KNOWS ;
:since 2021 ;
:source <http://example.org/node/1> ;
:target <http://example.org/node/2> .
<http://example.org/node/1> a :Person ;
:KNOWS <http://example.org/node/2> ;
:name "Alice" .
<http://example.org/node/2> a :Person ;
:name "Bob" .
How the mapping works
| GrafitoDB concept | RDF mapping |
|---|---|
| Node | Subject IRI (from the node's uri, or base_uri + node_prefix + id) |
| Label | rdf:type |
| Property (literal) | Predicate base_uri:<key> → literal object |
| Relationship | Direct triple (source) base_uri:<TYPE> (target) |
| Relationship with properties | Additionally reified: a rel/<id> resource typed as the relationship, carrying :source, :target and one predicate per property |
Relationships are reified so that edge properties (which plain RDF triples cannot
carry) are preserved. This is the inverse of what import_rdf recognises, so a
round-trip is loss-free (see Round-tripping).
Custom predicates and typed literals (__rdf__)
For fine-grained control over predicates, datatypes and language tags, add an
__rdf__ block to a node's (or relationship's) properties. It supports a JSON-LD
style @context, plus @id (IRI object), @value/@type/@lang (typed/tagged
literals), and lists:
db.create_node(
labels=['Person'],
properties={
'name': 'Alice',
'__rdf__': {
'@context': {'foaf': 'http://xmlns.com/foaf/0.1/'},
'foaf:homepage': {'@id': 'https://alice.example'},
'foaf:age': {'@value': 30, '@type': 'xsd:integer'},
'foaf:name': {'@value': 'Alice', '@lang': 'en'},
},
},
)
The __rdf__ key itself is never emitted as a literal; only its expanded triples are.
Importing RDF into GrafitoDB
From a Turtle string or file
from grafito import GrafitoDatabase
from grafito.integrations import import_turtle
db = GrafitoDatabase(':memory:')
summary = import_turtle(db, 'data.ttl', base_uri='http://example.org/')
print(summary) # {'nodes': 42, 'relationships': 87}
import_turtle accepts either a file path or a Turtle string as source,
and forwards format to rdflib (default "turtle"; also "xml", "json-ld",
"nt", "n3", "trig", …).
From an existing rdflib.Graph
from rdflib import Graph
from grafito.integrations import import_rdf
g = Graph()
g.parse('data.jsonld', format='json-ld')
db = GrafitoDatabase(':memory:')
import_rdf(db, g, base_uri='http://example.org/')
Import mapping
import_rdf projects RDF onto the property-graph model:
- A subject's
rdf:typevalues become node labels. - Triples whose object is a literal become node properties.
- Triples whose object is another resource become relationships.
- Grafito's reified edges (resources carrying
<base>sourceand<base>target) are recognised and imported as relationships that keep their literal predicates as edge properties; the redundant direct triple is de-duplicated. - When
store_uri=True(default), the original subject IRI is stored on the node'surifield, enabling loss-free re-export.
IRIs are shortened to compact label/property names by stripping base_uri, or
falling back to the fragment (#…) or last path segment.
It works on arbitrary RDF, not just Grafito's own export:
foaf = """
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
@prefix ex: <http://example.org/> .
ex:alice a foaf:Person ; foaf:name "Alice" ; foaf:knows ex:bob .
ex:bob a foaf:Person ; foaf:name "Bob" .
"""
db = GrafitoDatabase(':memory:')
import_turtle(db, foaf, base_uri='http://example.org/')
# -> node Person{name: Alice} -[knows]-> node Person{name: Bob}
Round-tripping
export and import are inverses, so a graph survives a full cycle:
turtle = export_turtle(db, base_uri='http://example.org/')
restored = GrafitoDatabase(':memory:')
import_turtle(restored, turtle, base_uri='http://example.org/')
# restored has the same nodes, labels, properties, relationships and edge properties
Other RDF formats (JSON-LD, N-Triples, RDF/XML…)
export_string and export_to_file reach any format rdflib supports. On files the
format is inferred from the extension (override with format=):
| Extension | Format | Extension | Format | |
|---|---|---|---|---|
.ttl |
turtle | .rdf / .xml |
xml | |
.jsonld / .json |
json-ld | .n3 |
n3 | |
.nt |
nt (N-Triples) | .trig |
trig | |
.nq |
nquads |
from grafito.integrations import export_string, export_to_file, import_from_file
# As a string, in any format
jsonld = export_string(db, base_uri='http://example.org/', format='json-ld')
# To a file (format inferred from the extension)
export_to_file(db, 'graph.jsonld', base_uri='http://example.org/')
# Import back, format inferred
restored = GrafitoDatabase(':memory:')
import_from_file(restored, 'graph.jsonld', base_uri='http://example.org/')
SPARQL queries
Grafito has no native SPARQL engine, but query_sparql materialises the graph as an
rdflib.Graph and runs the query there, giving full SPARQL 1.1
(SELECT / ASK / CONSTRUCT / DESCRIBE) over Grafito data:
from grafito.integrations import query_sparql
rows = query_sparql(db, """
PREFIX ex: <http://example.org/>
SELECT ?name ?age
WHERE { ?p a ex:Person ; ex:name ?name ; ex:age ?age }
ORDER BY DESC(?age)
""", base_uri='http://example.org/')
# -> [{'name': 'Alice', 'age': 30}, {'name': 'Bob', 'age': 25}]
query_sparql(db, 'PREFIX ex: <http://example.org/> ASK { ?x ex:name "Alice" }',
base_uri='http://example.org/')
# -> [{'boolean': True}]
Return shapes: SELECT → list of {variable: value} dicts; ASK → [{'boolean': ...}];
CONSTRUCT/DESCRIBE → list of {'s', 'p', 'o'} triples.
Note
The whole graph is loaded into memory for each call, so this is meant for interop
and small/medium graphs. For large graphs and traversals, use Cypher
(db.execute(...)) or the programmatic API, which query SQLite directly and use
indexes.
Comparing two graphs
graph_diff compares two databases by RDF isomorphism (via rdflib.compare),
correctly ignoring node-id ordering and blank-node labelling:
from grafito.integrations import graph_diff
graph_diff(db_a, db_b, base_uri='http://example.org/')
# -> {'isomorphic': True, 'in_both': 11, 'only_in_first': 0, 'only_in_second': 0}
Limitations
- Relationships are reified to carry edge properties (an extra resource per edge with properties). This is the standard RDF n-ary/reification pattern.
- Parallel edges collapse: RDF is a set of triples, so two relationships of the same type between the same pair of nodes cannot be distinguished after export.
- Blank nodes are imported as property-less nodes without a stable
uri. - Datatypes are inferred by
rdflibon import (toPython());xsd:decimalbecomes a Pythonfloat, and RDF types Grafito cannot store natively are coerced to strings.