Sophia wrapper: avoids relying on SPARQL queries

Uses simple rust code instead
pull/190/head
Tpt 3 years ago
parent db4c4bcb97
commit ec850b8ed4
  1. 124
      lib/src/sophia.rs

@ -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);

Loading…
Cancel
Save