RDF Graphs
The Graph
and MutableGraph
traits define how you interact with RDF graphs in Sophia.
Using graphs
RDF graphs are sets of triples,
so the most common thing you need to do with a graph is to iterate over its triples.
This is achieved with the Graph::triples
method:
use sophia::api::prelude::*;
use sophia::inmem::graph::LightGraph;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let g = LightGraph::new();
for result in g.triples() {
let triple = result?;
// do something with t;
}
Ok(()) }
Notice that Graph::triples
yields Result
s,
as some implementations of Graph
may fail at any point of the iteration.
When only a subset of the triples in the graph are of interest,
you will want to use the Graph::triples_matching
method:
use sophia::api::{ns::rdf, prelude::*, term::SimpleTerm};
use sophia::inmem::graph::LightGraph;
let graph = LightGraph::new();
// Utility closure to recognize IRIs in the schema.org namespace
let in_schema_org = |t: SimpleTerm| -> bool {
t.iri()
.map(|iri| iri.as_str().starts_with(("http://schema.org/")))
.unwrap_or(false)
};
// Iter over all instances of schema.org types
graph
.triples_matching(Any, [rdf::type_], in_schema_org)
.map(|res| { let [s, _, o] = res.unwrap().to_spo(); (s, o)})
.for_each(|(instance, typ)| {
// do something
})
Graph::triples_matching
accepts a large variety of parameters,
which will be described in more detail in the next chapter.
Graph
also provide methods to iterate over all subjects,
predicate
and object
in the graph,
as well as over all unique terms of a certain kind
(Graph::iris
, Graph::blank_nodes
, Graph::literals
, etc.).
Finally, it is possible to check whether a graph contains a specific triple with the method Graph::contains
.
Mutating graphs
Any implementation of Graph
that can be mutated should also implement MutableGraph
,
which comes with additional methods for modifying the graph.
Individual triples can be added to the graph (resp. removed from the graph)
with MutableGraph::insert
(resp. MutableGraph::remove
).
Inserting (resp. removing) a triple that is already (resp. not) present in the graph will be essentially a noop.
use sophia::{api::{ns::rdf, prelude::*}, iri::*};
/// Example: increment the rdf:value of a given subject
fn f<G: MutableGraph>(mut g: G) -> Result<(), Box<dyn std::error::Error>> {
let s = Iri::new_unchecked("https://example.org/foo");
let old_value: i32 = g.triples_matching([s], [rdf::value], Any)
.next()
.unwrap()?
.o()
.try_into_term()?;
g.remove(s, rdf::value, old_value)?;
g.insert(s, rdf::value, old_value + 1)?;
Ok(()) }
Batch modifications can also be performed on mutable graphs:
MutableGraph::insert_all
inserts all the triples from a triple source1;MutableGraph::remove_all
removes all the triples from a triple source1;MutableGraph::remove_matching
removes all the triples matching the parameters;MutableGraph::retain_matching
removes all the triples except those matching the parameters.
The parameters of remove_matching
and retain_matching
are similar to those of Graph::triples_matching
and are described in more detail in the next chapter.
Useful types implementing Graph
- slices of triples implement
Graph
; - standard collections (
Vec
,HashSet
andBTreeSet
) of triples implementGraph
andMutableGraph
; sophia::inmem::LightGraph
provides aGraph
andMutableGraph
implementation with a low memory footprint;sophia::inmem::FastGraph
provides aGraph
andMutableGraph
implementation designed for fast retrieval of any given triple.
Recipes for constructing graphs
Constructing and populating an empty graph
use sophia::{api::{ns::{Namespace, rdf}, prelude::*}, inmem::graph::FastGraph};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut g = FastGraph::new();
let ex = Namespace::new_unchecked("https://example.org/ns#");
let alice = ex.get_unchecked("alice");
let s = Namespace::new_unchecked("http://schema.org/");
g.insert(
&alice,
rdf::type_,
s.get_unchecked("Person")
)?;
g.insert(
&alice,
s.get_unchecked("name"),
"Alice"
)?;
Ok(()) }
Constructing a graph from a triple source1
use sophia::{api::prelude::*, inmem::graph::FastGraph, iri::Iri};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let big_graph = FastGraph::new();
// Extract all triples about 'alice' from big_graph in a new graph
let alice = Iri::new_unchecked("https://example.org/ns#alice");
let graph: FastGraph = big_graph
.triples_matching([alice], Any, Any)
.collect_triples()?;
Ok(()) }
NB: Only types implementing CollectibleGraph
can actually be constructed with the collected_triples
method as above.
However, most types implementing Graph
should implement CollectibleGraph
.
Constructing a graph from a file
use sophia::{api::prelude::*, inmem::graph::FastGraph, iri::Iri};
use std::{io::BufReader, fs::File};
use sophia::turtle::parser::turtle;
fn main() -> Result<(), Box<dyn std::error::Error>> {
dbg!(std::env::current_dir());
let f = BufReader::new(File::open("../sophia_doap.ttl")?);
let graph: FastGraph = turtle::parse_bufread(f)
.collect_triples()?;
Ok(()) }
For more about parsing (and serializing), see the corresponding chapter.
a TripleSource
is a fallible stream of triples,
such as those returned by Graph::triples
or Graph::triples_matching
,
or those returned by parsers.
In particular, any iterator of Result<T, E>
where T:
Triple
is a TripleSource
.