Merge pull request #46 from pchampin/sophia

a feature to enable Sophia's traits implementation in Oxigraph
pull/51/head
Thomas Tanon 4 years ago committed by GitHub
commit 0e3d37cb25
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 6
      lib/Cargo.toml
  2. 10
      lib/src/lib.rs
  3. 408
      lib/src/sophia/model.rs
  4. 806
      lib/src/sophia/store.rs
  5. 2
      lib/tests/rocksdb_store.rs
  6. 2
      lib/tests/sled_store.rs

@ -15,6 +15,10 @@ edition = "2018"
[package.metadata.docs.rs] [package.metadata.docs.rs]
all-features = true all-features = true
[features]
default = []
sophia = ["sophia_api"]
[dependencies] [dependencies]
rocksdb = { version = "0.14", optional = true } rocksdb = { version = "0.14", optional = true }
sled = { version = "0.34", optional = true } sled = { version = "0.34", optional = true }
@ -35,6 +39,7 @@ nom = "5"
peg = "0.6" peg = "0.6"
siphasher = "0.3" siphasher = "0.3"
lasso = {version="0.3", features=["multi-threaded"]} lasso = {version="0.3", features=["multi-threaded"]}
sophia_api = { version = "0.6.2", optional = true }
[target.'cfg(target_arch = "wasm32")'.dependencies] [target.'cfg(target_arch = "wasm32")'.dependencies]
js-sys = "0.3" js-sys = "0.3"
@ -43,6 +48,7 @@ getrandom = {version="0.1", features=["wasm-bindgen"]}
[dev-dependencies] [dev-dependencies]
rayon = "1" rayon = "1"
criterion = "0.3" criterion = "0.3"
sophia_api = { version = "0.6.2", features = ["test_macro"] }
[target.'cfg(target_arch = "wasm32")'.dev-dependencies] [target.'cfg(target_arch = "wasm32")'.dev-dependencies]
wasm-bindgen-test = "0.3" wasm-bindgen-test = "0.3"

@ -118,3 +118,13 @@ pub use crate::store::memory::MemoryStore;
pub use crate::store::rocksdb::RocksDbStore; pub use crate::store::rocksdb::RocksDbStore;
#[cfg(feature = "sled")] #[cfg(feature = "sled")]
pub use crate::store::sled::SledStore; pub use crate::store::sled::SledStore;
#[cfg(feature = "sophia")]
/// Provides implementation of [Sophia] traits for Oxigraph types,
/// if the `sophia` feature is enabled.
///
/// [Sophia]: https://docs.rs/sophia/latest/sophia/
mod sophia {
mod model;
mod store;
}

@ -0,0 +1,408 @@
//! This crate provides implementation of [Sophia] traits for the `model` module.
//! [Sophia]: https://docs.rs/sophia/latest/sophia/
use crate::model::*;
use sophia_api::term::*;
use std::fmt;
impl TTerm for BlankNode {
fn kind(&self) -> TermKind {
TermKind::BlankNode
}
fn value_raw(&self) -> RawValue<'_> {
self.as_str().into()
}
fn as_dyn(&self) -> &dyn TTerm {
self
}
}
impl TryCopyTerm for BlankNode {
type Error = SophiaToOxigraphConversionError;
fn try_copy<T>(other: &T) -> Result<Self, Self::Error>
where
T: TTerm + ?Sized,
{
match other.kind() {
TermKind::BlankNode => Ok(BlankNode::new_unchecked(other.value_raw().0)),
_ => Err(SophiaToOxigraphConversionError),
}
}
}
impl<'a> TTerm for BlankNodeRef<'a> {
fn kind(&self) -> TermKind {
TermKind::BlankNode
}
fn value_raw(&self) -> RawValue<'_> {
self.as_str().into()
}
fn as_dyn(&self) -> &dyn TTerm {
self
}
}
impl TTerm for Literal {
fn kind(&self) -> TermKind {
TermKind::Literal
}
fn value_raw(&self) -> RawValue<'_> {
Literal::value(self).into()
}
fn datatype(&self) -> Option<SimpleIri<'_>> {
Some(SimpleIri::new_unchecked(
Literal::datatype(self).as_str(),
None,
))
}
fn language(&self) -> Option<&str> {
Literal::language(self)
}
fn as_dyn(&self) -> &dyn TTerm {
self
}
}
impl TryCopyTerm for Literal {
type Error = SophiaToOxigraphConversionError;
fn try_copy<T>(other: &T) -> Result<Self, Self::Error>
where
T: TTerm + ?Sized,
{
match other.kind() {
TermKind::Literal => match other.language() {
Some(tag) => Ok(Literal::new_language_tagged_literal_unchecked(
other.value_raw().0,
tag,
)),
None => Ok(Literal::new_typed_literal(
other.value_raw().0,
other.datatype().unwrap(),
)),
},
_ => Err(SophiaToOxigraphConversionError),
}
}
}
impl<'a> TTerm for LiteralRef<'a> {
fn kind(&self) -> TermKind {
TermKind::Literal
}
fn value_raw(&self) -> RawValue<'_> {
LiteralRef::value(*self).into()
}
fn datatype(&self) -> Option<SimpleIri<'_>> {
Some(SimpleIri::new_unchecked(
LiteralRef::datatype(*self).as_str(),
None,
))
}
fn language(&self) -> Option<&str> {
LiteralRef::language(*self)
}
fn as_dyn(&self) -> &dyn TTerm {
self
}
}
impl TTerm for NamedNode {
fn kind(&self) -> TermKind {
TermKind::Iri
}
fn value_raw(&self) -> RawValue<'_> {
self.as_str().into()
}
fn as_dyn(&self) -> &dyn TTerm {
self
}
}
impl TryCopyTerm for NamedNode {
type Error = SophiaToOxigraphConversionError;
fn try_copy<T>(other: &T) -> Result<Self, Self::Error>
where
T: TTerm + ?Sized,
{
match other.kind() {
TermKind::Iri => Ok(NamedNode::new_unchecked(other.value())),
_ => Err(SophiaToOxigraphConversionError),
}
}
}
impl<'a> From<SimpleIri<'a>> for NamedNode {
fn from(other: SimpleIri<'a>) -> Self {
NamedNode::new_unchecked(other.value())
}
}
impl<'a> TTerm for NamedNodeRef<'a> {
fn kind(&self) -> TermKind {
TermKind::BlankNode
}
fn value_raw(&self) -> RawValue<'_> {
self.as_str().into()
}
fn as_dyn(&self) -> &dyn TTerm {
self
}
}
impl From<GraphName> for Option<Term> {
fn from(other: GraphName) -> Self {
use GraphName::*;
match other {
NamedNode(n) => Some(n.into()),
BlankNode(n) => Some(n.into()),
DefaultGraph => None,
}
}
}
impl<'a> From<GraphNameRef<'a>> for Option<TermRef<'a>> {
fn from(other: GraphNameRef<'a>) -> Self {
use GraphNameRef::*;
match other {
NamedNode(n) => Some(n.into()),
BlankNode(n) => Some(n.into()),
DefaultGraph => None,
}
}
}
impl TTerm for NamedOrBlankNode {
fn kind(&self) -> TermKind {
use NamedOrBlankNode::*;
match self {
NamedNode(_) => TermKind::Iri,
BlankNode(_) => TermKind::BlankNode,
}
}
fn value_raw(&self) -> RawValue<'_> {
use NamedOrBlankNode::*;
match self {
NamedNode(n) => n.value_raw(),
BlankNode(n) => n.value_raw(),
}
}
fn as_dyn(&self) -> &dyn TTerm {
use NamedOrBlankNode::*;
match self {
NamedNode(n) => n.as_dyn(),
BlankNode(n) => n.as_dyn(),
}
}
}
impl TryCopyTerm for NamedOrBlankNode {
type Error = SophiaToOxigraphConversionError;
fn try_copy<T>(other: &T) -> Result<Self, Self::Error>
where
T: TTerm + ?Sized,
{
match other.kind() {
TermKind::Iri => Ok(NamedNode::try_copy(other).unwrap().into()),
TermKind::BlankNode => Ok(BlankNode::try_copy(other).unwrap().into()),
_ => Err(SophiaToOxigraphConversionError),
}
}
}
impl<'a> TTerm for NamedOrBlankNodeRef<'a> {
fn kind(&self) -> TermKind {
use NamedOrBlankNodeRef::*;
match self {
NamedNode(_) => TermKind::Iri,
BlankNode(_) => TermKind::BlankNode,
}
}
fn value_raw(&self) -> RawValue<'_> {
use NamedOrBlankNodeRef::*;
match self {
NamedNode(n) => n.value_raw(),
BlankNode(n) => n.value_raw(),
}
}
fn as_dyn(&self) -> &dyn TTerm {
use NamedOrBlankNodeRef::*;
match self {
NamedNode(n) => n.as_dyn(),
BlankNode(n) => n.as_dyn(),
}
}
}
impl TTerm for Term {
fn kind(&self) -> TermKind {
use Term::*;
match self {
NamedNode(_) => TermKind::Iri,
BlankNode(_) => TermKind::BlankNode,
Literal(_) => TermKind::Literal,
}
}
fn value_raw(&self) -> RawValue<'_> {
use Term::*;
match self {
NamedNode(n) => n.value_raw(),
BlankNode(n) => n.value_raw(),
Literal(l) => l.value_raw(),
}
}
fn datatype(&self) -> Option<SimpleIri<'_>> {
use Term::*;
match self {
Literal(l) => TTerm::datatype(l),
_ => None,
}
}
fn language(&self) -> Option<&str> {
use Term::*;
match self {
Literal(l) => TTerm::language(l),
_ => None,
}
}
fn as_dyn(&self) -> &dyn TTerm {
use Term::*;
match self {
NamedNode(n) => n.as_dyn(),
BlankNode(n) => n.as_dyn(),
Literal(l) => l.as_dyn(),
}
}
}
impl TryCopyTerm for Term {
type Error = SophiaToOxigraphConversionError;
fn try_copy<T>(other: &T) -> Result<Self, Self::Error>
where
T: TTerm + ?Sized,
{
match other.kind() {
TermKind::Iri => Ok(NamedNode::try_copy(other).unwrap().into()),
TermKind::BlankNode => Ok(BlankNode::try_copy(other).unwrap().into()),
TermKind::Literal => Ok(Literal::try_copy(other).unwrap().into()),
_ => Err(SophiaToOxigraphConversionError),
}
}
}
impl<'a> TTerm for TermRef<'a> {
fn kind(&self) -> TermKind {
use TermRef::*;
match self {
NamedNode(_) => TermKind::Iri,
BlankNode(_) => TermKind::BlankNode,
Literal(_) => TermKind::Literal,
}
}
fn value_raw(&self) -> RawValue<'_> {
use TermRef::*;
match self {
NamedNode(n) => n.value_raw(),
BlankNode(n) => n.value_raw(),
Literal(l) => l.value_raw(),
}
}
fn datatype(&self) -> Option<SimpleIri<'_>> {
use TermRef::*;
match self {
Literal(l) => TTerm::datatype(l),
_ => None,
}
}
fn language(&self) -> Option<&str> {
use TermRef::*;
match self {
Literal(l) => TTerm::language(l),
_ => None,
}
}
fn as_dyn(&self) -> &dyn TTerm {
use TermRef::*;
match self {
NamedNode(n) => n.as_dyn(),
BlankNode(n) => n.as_dyn(),
Literal(l) => l.as_dyn(),
}
}
}
impl From<Quad> for ([Term; 3], Option<Term>) {
fn from(other: Quad) -> Self {
(
[other.subject.into(), other.predicate.into(), other.object],
other.graph_name.into(),
)
}
}
impl<'a> From<QuadRef<'a>> for ([TermRef<'a>; 3], Option<TermRef<'a>>) {
fn from(other: QuadRef<'a>) -> Self {
(
[other.subject.into(), other.predicate.into(), other.object],
other.graph_name.into(),
)
}
}
impl From<Triple> for [Term; 3] {
fn from(other: Triple) -> Self {
[other.subject.into(), other.predicate.into(), other.object]
}
}
impl<'a> From<TripleRef<'a>> for [TermRef<'a>; 3] {
fn from(other: TripleRef<'a>) -> Self {
[other.subject.into(), other.predicate.into(), other.object]
}
}
/// Error raised when trying to conpy a [Sophia]
/// term as an incompatible Oxigraph term
/// (e.g. a literal into `NamedNode`).
///
/// [Sophia]: https://docs.rs/sophia/latest/sophia/
#[derive(Clone, Copy, Debug)]
pub struct SophiaToOxigraphConversionError;
impl fmt::Display for SophiaToOxigraphConversionError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", self)
}
}
impl std::error::Error for SophiaToOxigraphConversionError {}

@ -0,0 +1,806 @@
//! This crate provides implementation of [Sophia] traits for the `store` module.
//! [Sophia]: https://docs.rs/sophia/latest/sophia/
use crate::model::*;
use crate::sparql::{EvaluationError, QueryOptions, QueryResults};
use crate::store::*;
use sophia_api::dataset::*;
use sophia_api::quad::stream::{QuadSource, StreamResult};
use sophia_api::quad::streaming_mode::{ByValue, StreamedQuad};
use sophia_api::term::{TTerm, TermKind, TryCopyTerm};
use std::collections::HashSet;
use std::convert::Infallible;
use std::hash::Hash;
use std::iter::empty;
type SophiaQuad = ([Term; 3], Option<Term>);
type StreamedSophiaQuad<'a> = StreamedQuad<'a, ByValue<SophiaQuad>>;
/// 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
macro_rules! sparql_to_hashset {
($store: ident, $err_map: ident, $sparql: expr) => {
//sparql_result_as_term_set($store, $sparql).map_err($err_map)
{
(|| -> Result<HashSet<Term>, EvaluationError> {
let q = $store.prepare_query($sparql, QueryOptions::default())?;
let r = q.exec()?;
if let QueryResults::Solutions(solutions) = r {
solutions
.map(|r| r.map(|v| v.get(0).unwrap().clone()))
.collect()
} else {
unreachable!()
}
})()
.map_err($err_map)
}
};
}
macro_rules! impl_dataset {
($store: ident, $error: ty, $quad_map: ident, $err_map: ident) => {
impl Dataset for $store {
type Quad = ByValue<SophiaQuad>;
type Error = $error;
fn quads(&self) -> DQuadSource<'_, Self> {
Box::new(
self.quads_for_pattern(None, None, None, None)
.map($quad_map),
)
}
fn quads_with_s<'s, TS>(&'s self, s: &'s TS) -> DQuadSource<'s, Self>
where
TS: TTerm + ?Sized,
{
let mut buf_s = String::new();
let s = convert_subject(s, &mut buf_s);
if s.is_none() {
Box::new(empty())
} else {
Box::new(self.quads_for_pattern(s, None, None, None).map($quad_map))
}
}
fn quads_with_p<'s, TP>(&'s self, p: &'s TP) -> DQuadSource<'s, Self>
where
TP: TTerm + ?Sized,
{
let mut buf_p = String::new();
let p = convert_predicate(p, &mut buf_p);
if p.is_none() {
Box::new(empty())
} else {
Box::new(self.quads_for_pattern(None, p, None, None).map($quad_map))
}
}
fn quads_with_o<'s, TS>(&'s self, o: &'s TS) -> DQuadSource<'s, Self>
where
TS: TTerm + ?Sized,
{
let mut buf_o = String::new();
let o = convert_object(o, &mut buf_o);
if o.is_none() {
Box::new(empty())
} else {
Box::new(self.quads_for_pattern(None, None, o, None).map($quad_map))
}
}
fn quads_with_g<'s, TS>(&'s self, g: Option<&'s TS>) -> DQuadSource<'s, Self>
where
TS: TTerm + ?Sized,
{
let mut buf_g = String::new();
let g = convert_graph_name(g, &mut buf_g);
if g.is_none() {
Box::new(empty())
} else {
Box::new(self.quads_for_pattern(None, None, None, g).map($quad_map))
}
}
fn quads_with_sp<'s, TS, TP>(&'s self, s: &'s TS, p: &'s TP) -> DQuadSource<'s, Self>
where
TS: TTerm + ?Sized,
TP: TTerm + ?Sized,
{
let mut buf_s = String::new();
let s = convert_subject(s, &mut buf_s);
let mut buf_p = String::new();
let p = convert_predicate(p, &mut buf_p);
if s.is_none() || p.is_none() {
Box::new(empty())
} else {
Box::new(self.quads_for_pattern(s, p, None, None).map($quad_map))
}
}
fn quads_with_so<'s, TS, TO>(&'s self, s: &'s TS, o: &'s TO) -> DQuadSource<'s, Self>
where
TS: TTerm + ?Sized,
TO: TTerm + ?Sized,
{
let mut buf_s = String::new();
let s = convert_subject(s, &mut buf_s);
let mut buf_o = String::new();
let o = convert_object(o, &mut buf_o);
if s.is_none() || o.is_none() {
Box::new(empty())
} else {
Box::new(self.quads_for_pattern(s, None, o, None).map($quad_map))
}
}
fn quads_with_sg<'s, TS, TG>(
&'s self,
s: &'s TS,
g: Option<&'s TG>,
) -> DQuadSource<'s, Self>
where
TS: TTerm + ?Sized,
TG: TTerm + ?Sized,
{
let mut buf_s = String::new();
let s = convert_subject(s, &mut buf_s);
let mut buf_g = String::new();
let g = convert_graph_name(g, &mut buf_g);
if s.is_none() || g.is_none() {
Box::new(empty())
} else {
Box::new(self.quads_for_pattern(s, None, None, g).map($quad_map))
}
}
fn quads_with_po<'s, TP, TO>(&'s self, p: &'s TP, o: &'s TO) -> DQuadSource<'s, Self>
where
TP: TTerm + ?Sized,
TO: TTerm + ?Sized,
{
let mut buf_p = String::new();
let p = convert_predicate(p, &mut buf_p);
let mut buf_o = String::new();
let o = convert_object(o, &mut buf_o);
if p.is_none() || o.is_none() {
Box::new(empty())
} else {
Box::new(self.quads_for_pattern(None, p, o, None).map($quad_map))
}
}
fn quads_with_pg<'s, TP, TG>(
&'s self,
p: &'s TP,
g: Option<&'s TG>,
) -> DQuadSource<'s, Self>
where
TP: TTerm + ?Sized,
TG: TTerm + ?Sized,
{
let mut buf_p = String::new();
let p = convert_predicate(p, &mut buf_p);
let mut buf_g = String::new();
let g = convert_graph_name(g, &mut buf_g);
if p.is_none() || g.is_none() {
Box::new(empty())
} else {
Box::new(self.quads_for_pattern(None, p, None, g).map($quad_map))
}
}
fn quads_with_og<'s, TO, TG>(
&'s self,
o: &'s TO,
g: Option<&'s TG>,
) -> DQuadSource<'s, Self>
where
TO: TTerm + ?Sized,
TG: TTerm + ?Sized,
{
let mut buf_o = String::new();
let o = convert_object(o, &mut buf_o);
let mut buf_g = String::new();
let g = convert_graph_name(g, &mut buf_g);
if o.is_none() || g.is_none() {
Box::new(empty())
} else {
Box::new(self.quads_for_pattern(None, None, o, g).map($quad_map))
}
}
fn quads_with_spo<'s, TS, TP, TO>(
&'s self,
s: &'s TS,
p: &'s TP,
o: &'s TO,
) -> DQuadSource<'s, Self>
where
TS: TTerm + ?Sized,
TP: TTerm + ?Sized,
TO: TTerm + ?Sized,
{
let mut buf_s = String::new();
let s = convert_subject(s, &mut buf_s);
let mut buf_p = String::new();
let p = convert_predicate(p, &mut buf_p);
let mut buf_o = String::new();
let o = convert_object(o, &mut buf_o);
if s.is_none() || p.is_none() || o.is_none() {
Box::new(empty())
} else {
Box::new(self.quads_for_pattern(s, p, o, None).map($quad_map))
}
}
fn quads_with_spg<'s, TS, TP, TG>(
&'s self,
s: &'s TS,
p: &'s TP,
g: Option<&'s TG>,
) -> DQuadSource<'s, Self>
where
TS: TTerm + ?Sized,
TP: TTerm + ?Sized,
TG: TTerm + ?Sized,
{
let mut buf_s = String::new();
let s = convert_subject(s, &mut buf_s);
let mut buf_p = String::new();
let p = convert_predicate(p, &mut buf_p);
let mut buf_g = String::new();
let g = convert_graph_name(g, &mut buf_g);
if s.is_none() || p.is_none() || g.is_none() {
Box::new(empty())
} else {
Box::new(self.quads_for_pattern(s, p, None, g).map($quad_map))
}
}
fn quads_with_sog<'s, TS, TO, TG>(
&'s self,
s: &'s TS,
o: &'s TO,
g: Option<&'s TG>,
) -> DQuadSource<'s, Self>
where
TS: TTerm + ?Sized,
TO: TTerm + ?Sized,
TG: TTerm + ?Sized,
{
let mut buf_s = String::new();
let s = convert_subject(s, &mut buf_s);
let mut buf_o = String::new();
let o = convert_object(o, &mut buf_o);
let mut buf_g = String::new();
let g = convert_graph_name(g, &mut buf_g);
if s.is_none() || o.is_none() || g.is_none() {
Box::new(empty())
} else {
Box::new(self.quads_for_pattern(s, None, o, g).map($quad_map))
}
}
fn quads_with_pog<'s, TP, TO, TG>(
&'s self,
p: &'s TP,
o: &'s TO,
g: Option<&'s TG>,
) -> DQuadSource<'s, Self>
where
TP: TTerm + ?Sized,
TO: TTerm + ?Sized,
TG: TTerm + ?Sized,
{
let mut buf_p = String::new();
let p = convert_predicate(p, &mut buf_p);
let mut buf_o = String::new();
let o = convert_object(o, &mut buf_o);
let mut buf_g = String::new();
let g = convert_graph_name(g, &mut buf_g);
if p.is_none() || o.is_none() || g.is_none() {
Box::new(empty())
} else {
Box::new(self.quads_for_pattern(None, p, o, g).map($quad_map))
}
}
fn quads_with_spog<'s, TS, TP, TO, TG>(
&'s self,
s: &'s TS,
p: &'s TP,
o: &'s TO,
g: Option<&'s TG>,
) -> DQuadSource<'s, Self>
where
TS: TTerm + ?Sized,
TP: TTerm + ?Sized,
TO: TTerm + ?Sized,
TG: TTerm + ?Sized,
{
let mut buf_s = String::new();
let s = convert_subject(s, &mut buf_s);
let mut buf_p = String::new();
let p = convert_predicate(p, &mut buf_p);
let mut buf_o = String::new();
let o = convert_object(o, &mut buf_o);
let mut buf_g = String::new();
let g = convert_graph_name(g, &mut buf_g);
if s.is_none() || p.is_none() || o.is_none() || g.is_none() {
Box::new(empty())
} else {
Box::new(self.quads_for_pattern(s, p, o, g).map($quad_map))
}
}
fn subjects(&self) -> DResultTermSet<Self>
where
DTerm<Self>: Clone + Eq + Hash,
{
sparql_to_hashset!(
self,
$err_map,
"SELECT DISTINCT ?s {{?s ?p ?o} UNION { GRAPH ?g {?s ?p ?o}}}"
)
}
fn predicates(&self) -> DResultTermSet<Self>
where
DTerm<Self>: Clone + Eq + Hash,
{
sparql_to_hashset!(
self,
$err_map,
"SELECT DISTINCT ?p {{?s ?p ?o} UNION { GRAPH ?g {?s ?p ?o}}}"
)
}
fn objects(&self) -> DResultTermSet<Self>
where
DTerm<Self>: Clone + Eq + Hash,
{
sparql_to_hashset!(
self,
$err_map,
"SELECT DISTINCT ?o {{?s ?p ?o} UNION { GRAPH ?g {?s ?p ?o}}}"
)
}
fn graph_names(&self) -> DResultTermSet<Self>
where
DTerm<Self>: Clone + Eq + Hash,
{
sparql_to_hashset!(self, $err_map, "SELECT DISTINCT ?g {GRAPH ?g {?s ?p ?o}}")
}
fn iris(&self) -> DResultTermSet<Self>
where
DTerm<Self>: Clone + Eq + Hash,
{
sparql_to_hashset!(
self,
$err_map,
"SELECT DISTINCT ?iri {
{?iri ?p ?o} UNION
{?s ?iri ?o} UNION
{?s ?p ?iri} UNION
{GRAPH ?iri {?s ?p ?o}} UNION
{GRAPH ?s {?iri ?p ?o}} UNION
{GRAPH ?g {?s ?iri ?o}} UNION
{GRAPH ?g {?s ?p ?iri}}
FILTER isIRI(?iri)
}"
)
}
fn bnodes(&self) -> DResultTermSet<Self>
where
DTerm<Self>: Clone + Eq + Hash,
{
sparql_to_hashset!(
self,
$err_map,
"SELECT DISTINCT ?bn {
{?bn ?p ?o} UNION
{?s ?p ?bn} UNION
{GRAPH ?bn {?s ?p ?o}} UNION
{GRAPH ?s {?bn ?p ?o}} UNION
{GRAPH ?g {?s ?p ?bn}}
FILTER isBlank(?bn)
}"
)
}
fn literals(&self) -> DResultTermSet<Self>
where
DTerm<Self>: Clone + Eq + Hash,
{
sparql_to_hashset!(
self,
$err_map,
"SELECT DISTINCT ?lit {
{?s ?p ?lit} UNION
{ GRAPH ?g {?s ?p ?lit}}
FILTER isLiteral(?lit)
}"
)
}
fn variables(&self) -> DResultTermSet<Self>
where
DTerm<Self>: Clone + Eq + Hash,
{
Ok(std::collections::HashSet::new())
}
}
};
}
mod mem {
use super::*;
impl_dataset!(
MemoryStore,
Infallible,
infallible_quad_map,
infallible_err_map
);
impl MutableDataset for MemoryStore {
type MutationError = Infallible;
fn insert<TS, TP, TO, TG>(
&mut self,
s: &TS,
p: &TP,
o: &TO,
g: Option<&TG>,
) -> MDResult<Self, bool>
where
TS: TTerm + ?Sized,
TP: TTerm + ?Sized,
TO: TTerm + ?Sized,
TG: TTerm + ?Sized,
{
let quad = match convert_quad(s, p, o, g) {
Some(quad) => quad,
None => return Ok(false),
};
MemoryStore::insert(self, quad);
Ok(true)
}
fn remove<TS, TP, TO, TG>(
&mut self,
s: &TS,
p: &TP,
o: &TO,
g: Option<&TG>,
) -> MDResult<Self, bool>
where
TS: TTerm + ?Sized,
TP: TTerm + ?Sized,
TO: TTerm + ?Sized,
TG: TTerm + ?Sized,
{
let mut buf_s = String::new();
let mut buf_p = String::new();
let mut buf_o = String::new();
let mut buf_g = String::new();
let quadref =
match convert_quadref(s, p, o, g, &mut buf_s, &mut buf_p, &mut buf_o, &mut buf_g) {
Some(quad) => quad,
None => return Ok(false),
};
MemoryStore::remove(self, quadref);
Ok(true)
}
}
impl CollectibleDataset for MemoryStore {
fn from_quad_source<QS: QuadSource>(
quads: QS,
) -> StreamResult<Self, QS::Error, Self::Error> {
let mut d = MemoryStore::new();
d.insert_all(quads)?;
Ok(d)
}
}
#[cfg(test)]
sophia_api::test_dataset_impl!(test, MemoryStore, false, false);
}
#[cfg(feature = "sled")]
mod sled {
use super::*;
impl_dataset!(SledStore, std::io::Error, io_quad_map, io_err_map);
impl MutableDataset for SledStore {
type MutationError = std::io::Error;
fn insert<TS, TP, TO, TG>(
&mut self,
s: &TS,
p: &TP,
o: &TO,
g: Option<&TG>,
) -> MDResult<Self, bool>
where
TS: TTerm + ?Sized,
TP: TTerm + ?Sized,
TO: TTerm + ?Sized,
TG: TTerm + ?Sized,
{
let mut buf_s = String::new();
let mut buf_p = String::new();
let mut buf_o = String::new();
let mut buf_g = String::new();
let quadref =
match convert_quadref(s, p, o, g, &mut buf_s, &mut buf_p, &mut buf_o, &mut buf_g) {
Some(quad) => quad,
None => return Ok(false),
};
SledStore::insert(self, quadref).map(|_| true)
}
fn remove<TS, TP, TO, TG>(
&mut self,
s: &TS,
p: &TP,
o: &TO,
g: Option<&TG>,
) -> MDResult<Self, bool>
where
TS: TTerm + ?Sized,
TP: TTerm + ?Sized,
TO: TTerm + ?Sized,
TG: TTerm + ?Sized,
{
let mut buf_s = String::new();
let mut buf_p = String::new();
let mut buf_o = String::new();
let mut buf_g = String::new();
let quadref =
match convert_quadref(s, p, o, g, &mut buf_s, &mut buf_p, &mut buf_o, &mut buf_g) {
Some(quad) => quad,
None => return Ok(false),
};
SledStore::remove(self, quadref).map(|_| true)
}
}
impl CollectibleDataset for SledStore {
fn from_quad_source<QS: QuadSource>(
quads: QS,
) -> StreamResult<Self, QS::Error, Self::Error> {
let mut d =
SledStore::new().map_err(sophia_api::quad::stream::StreamError::SinkError)?;
d.insert_all(quads)?;
Ok(d)
}
}
#[cfg(test)]
sophia_api::test_dataset_impl!(test, SledStore, false, false);
}
#[cfg(feature = "rocksdb")]
mod rocksdb {
use super::*;
impl_dataset!(RocksDbStore, std::io::Error, io_quad_map, io_err_map);
impl MutableDataset for RocksDbStore {
type MutationError = std::io::Error;
fn insert<TS, TP, TO, TG>(
&mut self,
s: &TS,
p: &TP,
o: &TO,
g: Option<&TG>,
) -> MDResult<Self, bool>
where
TS: TTerm + ?Sized,
TP: TTerm + ?Sized,
TO: TTerm + ?Sized,
TG: TTerm + ?Sized,
{
let mut buf_s = String::new();
let mut buf_p = String::new();
let mut buf_o = String::new();
let mut buf_g = String::new();
let quadref =
match convert_quadref(s, p, o, g, &mut buf_s, &mut buf_p, &mut buf_o, &mut buf_g) {
Some(quad) => quad,
None => return Ok(false),
};
RocksDbStore::insert(self, quadref).map(|_| true)
}
fn remove<TS, TP, TO, TG>(
&mut self,
s: &TS,
p: &TP,
o: &TO,
g: Option<&TG>,
) -> MDResult<Self, bool>
where
TS: TTerm + ?Sized,
TP: TTerm + ?Sized,
TO: TTerm + ?Sized,
TG: TTerm + ?Sized,
{
let mut buf_s = String::new();
let mut buf_p = String::new();
let mut buf_o = String::new();
let mut buf_g = String::new();
let quadref =
match convert_quadref(s, p, o, g, &mut buf_s, &mut buf_p, &mut buf_o, &mut buf_g) {
Some(quad) => quad,
None => return Ok(false),
};
RocksDbStore::remove(self, quadref).map(|_| true)
}
}
}
// helper functions
fn infallible_quad_map<'a>(q: Quad) -> Result<StreamedSophiaQuad<'a>, Infallible> {
let q: SophiaQuad = q.into();
Ok(StreamedQuad::by_value(q))
}
fn infallible_err_map(_: EvaluationError) -> Infallible {
panic!("Unexpected error")
}
#[cfg(any(feature = "rocksdb", feature = "sled"))]
fn io_quad_map<'a>(
res: Result<Quad, std::io::Error>,
) -> Result<StreamedSophiaQuad<'a>, std::io::Error> {
res.map(|q| {
let q: SophiaQuad = q.into();
StreamedQuad::by_value(q)
})
}
#[cfg(any(feature = "rocksdb", feature = "sled"))]
fn io_err_map(err: EvaluationError) -> std::io::Error {
match err {
EvaluationError::Io(err) => err,
_ => panic!("Unexpected error"),
}
}
fn convert_subject<'a, T>(term: &'a T, buffer: &'a mut String) -> Option<NamedOrBlankNodeRef<'a>>
where
T: TTerm + ?Sized + 'a,
{
match term.kind() {
TermKind::Iri => Some(convert_iri(term, buffer).into()),
TermKind::BlankNode => Some(BlankNodeRef::new_unchecked(term.value_raw().0).into()),
_ => None,
}
}
fn convert_predicate<'a, T>(term: &'a T, buffer: &'a mut String) -> Option<NamedNodeRef<'a>>
where
T: TTerm + ?Sized + 'a,
{
match term.kind() {
TermKind::Iri => Some(convert_iri(term, buffer)),
_ => None,
}
}
fn convert_object<'a, T>(term: &'a T, buffer: &'a mut String) -> Option<TermRef<'a>>
where
T: TTerm + ?Sized + 'a,
{
match term.kind() {
TermKind::Iri => Some(convert_iri(term, buffer).into()),
TermKind::BlankNode => Some(BlankNodeRef::new_unchecked(term.value_raw().0).into()),
TermKind::Literal => {
let value = term.value_raw().0;
let lit = match term.language() {
Some(tag) => LiteralRef::new_language_tagged_literal_unchecked(value, tag),
None => {
let (ns, suffix) = term.datatype().unwrap().destruct();
let datatype = convert_iri_raw(ns, suffix, buffer);
LiteralRef::new_typed_literal(value, datatype)
}
};
Some(lit.into())
}
_ => None,
}
}
fn convert_graph_name<'a, T>(
graph_name: Option<&'a T>,
buffer: &'a mut String,
) -> Option<GraphNameRef<'a>>
where
T: TTerm + ?Sized + 'a,
{
match graph_name {
None => Some(GraphNameRef::DefaultGraph),
Some(term) => match term.kind() {
TermKind::Iri => Some(convert_iri(term, buffer).into()),
TermKind::BlankNode => Some(BlankNodeRef::new_unchecked(term.value_raw().0).into()),
_ => None,
},
}
}
fn convert_iri<'a, T>(term: &'a T, buffer: &'a mut String) -> NamedNodeRef<'a>
where
T: TTerm + ?Sized + 'a,
{
debug_assert_eq!(term.kind(), TermKind::Iri);
let raw = term.value_raw();
convert_iri_raw(raw.0, raw.1, buffer)
}
fn convert_iri_raw<'a>(
ns: &'a str,
suffix: Option<&'a str>,
buffer: &'a mut String,
) -> NamedNodeRef<'a> {
let iri: &'a str = match suffix {
Some(suffix) => {
buffer.clear();
buffer.push_str(ns);
buffer.push_str(suffix);
buffer
}
None => ns,
};
NamedNodeRef::new_unchecked(iri)
}
fn convert_quad<TS, TP, TO, TG>(s: &TS, p: &TP, o: &TO, g: Option<&TG>) -> Option<Quad>
where
TS: TTerm + ?Sized,
TP: TTerm + ?Sized,
TO: TTerm + ?Sized,
TG: TTerm + ?Sized,
{
let s = match NamedOrBlankNode::try_copy(s) {
Ok(s) => s,
Err(_) => return None,
};
let p = match NamedNode::try_copy(p) {
Ok(p) => p,
Err(_) => return None,
};
let o = match Term::try_copy(o) {
Ok(o) => o,
Err(_) => return None,
};
let g = match g {
None => GraphName::DefaultGraph,
Some(g) => match NamedOrBlankNode::try_copy(g) {
Ok(g) => g.into(),
Err(_) => return None,
},
};
Some(Quad::new(s, p, o, g))
}
fn convert_quadref<'a, TS, TP, TO, TG>(
s: &'a TS,
p: &'a TP,
o: &'a TO,
g: Option<&'a TG>,
buf_s: &'a mut String,
buf_p: &'a mut String,
buf_o: &'a mut String,
buf_g: &'a mut String,
) -> Option<QuadRef<'a>>
where
TS: TTerm + ?Sized,
TP: TTerm + ?Sized,
TO: TTerm + ?Sized,
TG: TTerm + ?Sized,
{
let s = match convert_subject(s, buf_s) {
Some(s) => s,
None => return None,
};
let p = match convert_predicate(p, buf_p) {
Some(p) => p,
None => return None,
};
let o = match convert_object(o, buf_o) {
Some(o) => o,
None => return None,
};
let g = match convert_graph_name(g, buf_g) {
Some(g) => g,
None => return None,
};
Some(QuadRef::new(s, p, o, g))
}

@ -1,3 +1,5 @@
#![cfg(features = "rocksdb")]
use oxigraph::model::vocab::{rdf, xsd}; use oxigraph::model::vocab::{rdf, xsd};
use oxigraph::model::*; use oxigraph::model::*;
use oxigraph::RocksDbStore; use oxigraph::RocksDbStore;

@ -1,3 +1,5 @@
#![cfg(features = "sled")]
use oxigraph::io::{DatasetFormat, GraphFormat}; use oxigraph::io::{DatasetFormat, GraphFormat};
use oxigraph::model::vocab::{rdf, xsd}; use oxigraph::model::vocab::{rdf, xsd};
use oxigraph::model::*; use oxigraph::model::*;

Loading…
Cancel
Save