|
|
@ -1,9 +1,9 @@ |
|
|
|
//! This crate provides implementation of [Sophia](https://docs.rs/sophia/) traits for the `store` module.
|
|
|
|
//! This crate provides implementation of [Sophia](https://docs.rs/sophia/) traits for the `store` module.
|
|
|
|
|
|
|
|
|
|
|
|
use crate::model::{ |
|
|
|
use crate::model::{ |
|
|
|
BlankNodeRef, GraphNameRef, LiteralRef, NamedNodeRef, Quad, QuadRef, SubjectRef, Term, TermRef, |
|
|
|
BlankNodeRef, GraphName, GraphNameRef, LiteralRef, NamedNodeRef, Quad, QuadRef, Subject, |
|
|
|
|
|
|
|
SubjectRef, Term, TermRef, |
|
|
|
}; |
|
|
|
}; |
|
|
|
use crate::sparql::{EvaluationError, QueryResults}; |
|
|
|
|
|
|
|
use crate::store::Store; |
|
|
|
use crate::store::Store; |
|
|
|
use sophia_api::dataset::{ |
|
|
|
use sophia_api::dataset::{ |
|
|
|
CollectibleDataset, DQuadSource, DResultTermSet, DTerm, Dataset, MdResult, MutableDataset, |
|
|
|
CollectibleDataset, DQuadSource, DResultTermSet, DTerm, Dataset, MdResult, MutableDataset, |
|
|
@ -13,7 +13,7 @@ use sophia_api::quad::streaming_mode::{ByValue, StreamedQuad}; |
|
|
|
use sophia_api::term::{TTerm, TermKind}; |
|
|
|
use sophia_api::term::{TTerm, TermKind}; |
|
|
|
use std::collections::HashSet; |
|
|
|
use std::collections::HashSet; |
|
|
|
use std::hash::Hash; |
|
|
|
use std::hash::Hash; |
|
|
|
use std::io::{Error, ErrorKind}; |
|
|
|
use std::io::Error; |
|
|
|
use std::iter::empty; |
|
|
|
use std::iter::empty; |
|
|
|
|
|
|
|
|
|
|
|
type SophiaQuad = ([Term; 3], Option<Term>); |
|
|
|
type SophiaQuad = ([Term; 3], Option<Term>); |
|
|
@ -288,81 +288,85 @@ impl Dataset for Store { |
|
|
|
where |
|
|
|
where |
|
|
|
DTerm<Self>: Clone + Eq + Hash, |
|
|
|
DTerm<Self>: Clone + Eq + Hash, |
|
|
|
{ |
|
|
|
{ |
|
|
|
sparql_to_hashset( |
|
|
|
self.iter() |
|
|
|
self, |
|
|
|
.map(|r| r.map(|q| q.subject.into())) |
|
|
|
"SELECT DISTINCT ?s {{?s ?p ?o} UNION { GRAPH ?g {?s ?p ?o}}}", |
|
|
|
.collect::<Result<_, _>>() |
|
|
|
) |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
fn predicates(&self) -> DResultTermSet<Self> |
|
|
|
fn predicates(&self) -> DResultTermSet<Self> |
|
|
|
where |
|
|
|
where |
|
|
|
DTerm<Self>: Clone + Eq + Hash, |
|
|
|
DTerm<Self>: Clone + Eq + Hash, |
|
|
|
{ |
|
|
|
{ |
|
|
|
sparql_to_hashset( |
|
|
|
self.iter() |
|
|
|
self, |
|
|
|
.map(|r| r.map(|q| q.predicate.into())) |
|
|
|
"SELECT DISTINCT ?p {{?s ?p ?o} UNION { GRAPH ?g {?s ?p ?o}}}", |
|
|
|
.collect::<Result<_, _>>() |
|
|
|
) |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
fn objects(&self) -> DResultTermSet<Self> |
|
|
|
fn objects(&self) -> DResultTermSet<Self> |
|
|
|
where |
|
|
|
where |
|
|
|
DTerm<Self>: Clone + Eq + Hash, |
|
|
|
DTerm<Self>: Clone + Eq + Hash, |
|
|
|
{ |
|
|
|
{ |
|
|
|
sparql_to_hashset( |
|
|
|
self.iter() |
|
|
|
self, |
|
|
|
.map(|r| r.map(|q| q.object)) |
|
|
|
"SELECT DISTINCT ?o {{?s ?p ?o} UNION { GRAPH ?g {?s ?p ?o}}}", |
|
|
|
.collect::<Result<_, _>>() |
|
|
|
) |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
fn graph_names(&self) -> DResultTermSet<Self> |
|
|
|
fn graph_names(&self) -> DResultTermSet<Self> |
|
|
|
where |
|
|
|
where |
|
|
|
DTerm<Self>: Clone + Eq + Hash, |
|
|
|
DTerm<Self>: Clone + Eq + Hash, |
|
|
|
{ |
|
|
|
{ |
|
|
|
sparql_to_hashset(self, "SELECT DISTINCT ?g {GRAPH ?g {?s ?p ?o}}") |
|
|
|
self.named_graphs() |
|
|
|
|
|
|
|
.map(|r| r.map(|g| g.into())) |
|
|
|
|
|
|
|
.collect::<Result<_, _>>() |
|
|
|
} |
|
|
|
} |
|
|
|
fn iris(&self) -> DResultTermSet<Self> |
|
|
|
fn iris(&self) -> DResultTermSet<Self> |
|
|
|
where |
|
|
|
where |
|
|
|
DTerm<Self>: Clone + Eq + Hash, |
|
|
|
DTerm<Self>: Clone + Eq + Hash, |
|
|
|
{ |
|
|
|
{ |
|
|
|
sparql_to_hashset( |
|
|
|
let mut iris = HashSet::new(); |
|
|
|
self, |
|
|
|
for q in self.iter() { |
|
|
|
"SELECT DISTINCT ?iri { |
|
|
|
let q = q?; |
|
|
|
{?iri ?p ?o} UNION |
|
|
|
if let Subject::NamedNode(s) = q.subject { |
|
|
|
{?s ?iri ?o} UNION |
|
|
|
iris.insert(s.into()); |
|
|
|
{?s ?p ?iri} UNION |
|
|
|
} |
|
|
|
{GRAPH ?iri {?s ?p ?o}} UNION |
|
|
|
iris.insert(q.predicate.into()); |
|
|
|
{GRAPH ?s {?iri ?p ?o}} UNION |
|
|
|
if let Term::NamedNode(o) = q.object { |
|
|
|
{GRAPH ?g {?s ?iri ?o}} UNION |
|
|
|
iris.insert(o.into()); |
|
|
|
{GRAPH ?g {?s ?p ?iri}} |
|
|
|
} |
|
|
|
FILTER isIRI(?iri) |
|
|
|
if let GraphName::NamedNode(g) = q.graph_name { |
|
|
|
}", |
|
|
|
iris.insert(g.into()); |
|
|
|
) |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
Ok(iris) |
|
|
|
} |
|
|
|
} |
|
|
|
fn bnodes(&self) -> DResultTermSet<Self> |
|
|
|
fn bnodes(&self) -> DResultTermSet<Self> |
|
|
|
where |
|
|
|
where |
|
|
|
DTerm<Self>: Clone + Eq + Hash, |
|
|
|
DTerm<Self>: Clone + Eq + Hash, |
|
|
|
{ |
|
|
|
{ |
|
|
|
sparql_to_hashset( |
|
|
|
let mut bnodes = HashSet::new(); |
|
|
|
self, |
|
|
|
for q in self.iter() { |
|
|
|
"SELECT DISTINCT ?bn { |
|
|
|
let q = q?; |
|
|
|
{?bn ?p ?o} UNION |
|
|
|
if let Subject::BlankNode(s) = q.subject { |
|
|
|
{?s ?p ?bn} UNION |
|
|
|
bnodes.insert(s.into()); |
|
|
|
{GRAPH ?bn {?s ?p ?o}} UNION |
|
|
|
} |
|
|
|
{GRAPH ?s {?bn ?p ?o}} UNION |
|
|
|
if let Term::BlankNode(o) = q.object { |
|
|
|
{GRAPH ?g {?s ?p ?bn}} |
|
|
|
bnodes.insert(o.into()); |
|
|
|
FILTER isBlank(?bn) |
|
|
|
} |
|
|
|
}", |
|
|
|
if let GraphName::BlankNode(g) = q.graph_name { |
|
|
|
) |
|
|
|
bnodes.insert(g.into()); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
Ok(bnodes) |
|
|
|
} |
|
|
|
} |
|
|
|
fn literals(&self) -> DResultTermSet<Self> |
|
|
|
fn literals(&self) -> DResultTermSet<Self> |
|
|
|
where |
|
|
|
where |
|
|
|
DTerm<Self>: Clone + Eq + Hash, |
|
|
|
DTerm<Self>: Clone + Eq + Hash, |
|
|
|
{ |
|
|
|
{ |
|
|
|
sparql_to_hashset( |
|
|
|
let mut literals = HashSet::new(); |
|
|
|
self, |
|
|
|
for q in self.iter() { |
|
|
|
"SELECT DISTINCT ?lit { |
|
|
|
let q = q?; |
|
|
|
{?s ?p ?lit} UNION |
|
|
|
if let Term::Literal(o) = q.object { |
|
|
|
{ GRAPH ?g {?s ?p ?lit}} |
|
|
|
literals.insert(o.into()); |
|
|
|
FILTER isLiteral(?lit) |
|
|
|
} |
|
|
|
}", |
|
|
|
} |
|
|
|
) |
|
|
|
Ok(literals) |
|
|
|
} |
|
|
|
} |
|
|
|
fn variables(&self) -> DResultTermSet<Self> |
|
|
|
fn variables(&self) -> DResultTermSet<Self> |
|
|
|
where |
|
|
|
where |
|
|
@ -441,13 +445,6 @@ fn io_quad_map<'a>(res: Result<Quad, Error>) -> Result<StreamedSophiaQuad<'a>, E |
|
|
|
}) |
|
|
|
}) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fn io_err_map(err: EvaluationError) -> Error { |
|
|
|
|
|
|
|
match err { |
|
|
|
|
|
|
|
EvaluationError::Io(err) => err, |
|
|
|
|
|
|
|
err => Error::new(ErrorKind::InvalidInput, err), |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn convert_subject<'a, T>(term: &'a T, buffer: &'a mut String) -> Option<SubjectRef<'a>> |
|
|
|
fn convert_subject<'a, T>(term: &'a T, buffer: &'a mut String) -> Option<SubjectRef<'a>> |
|
|
|
where |
|
|
|
where |
|
|
|
T: TTerm + ?Sized + 'a, |
|
|
|
T: TTerm + ?Sized + 'a, |
|
|
@ -569,22 +566,5 @@ where |
|
|
|
Some(QuadRef::new(s, p, o, g)) |
|
|
|
Some(QuadRef::new(s, p, o, g)) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Execute a SPARQL query in a store, and return the result as a `HashSet`,
|
|
|
|
|
|
|
|
/// mapping the error (if any) through the given function.
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// # Precondition
|
|
|
|
|
|
|
|
/// + the query must be a SELECT query with a single selected variable
|
|
|
|
|
|
|
|
/// + it must not produce NULL results
|
|
|
|
|
|
|
|
fn sparql_to_hashset(store: &Store, sparql: &str) -> Result<HashSet<Term>, Error> { |
|
|
|
|
|
|
|
if let QueryResults::Solutions(solutions) = store.query(sparql).map_err(io_err_map)? { |
|
|
|
|
|
|
|
solutions |
|
|
|
|
|
|
|
.map(|r| r.map(|v| v.get(0).unwrap().clone())) |
|
|
|
|
|
|
|
.collect::<Result<_, _>>() |
|
|
|
|
|
|
|
.map_err(io_err_map) |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
unreachable!() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[cfg(test)] |
|
|
|
#[cfg(test)] |
|
|
|
sophia_api::test_dataset_impl!(test, Store, false, false); |
|
|
|
sophia_api::test_dataset_impl!(test, Store, false, false); |
|
|
|