Basic RDF-star support

No parsing and SPARQL support yet
pull/171/head
Tpt 3 years ago
parent 820ea62424
commit a97250dcce
  1. 2
      lib/README.md
  2. 4
      lib/benches/store.rs
  3. 2
      lib/src/lib.rs
  4. 151
      lib/src/model/dataset.rs
  5. 135
      lib/src/model/interning.rs
  6. 116
      lib/src/model/sophia.rs
  7. 184
      lib/src/model/triple.rs
  8. 25
      lib/src/sparql/csv_results.rs
  9. 17
      lib/src/sparql/eval.rs
  10. 59
      lib/src/sparql/json_results.rs
  11. 2
      lib/src/sparql/update.rs
  12. 59
      lib/src/sparql/xml_results.rs
  13. 24
      lib/src/storage/binary_encoder.rs
  14. 91
      lib/src/storage/numeric_encoder.rs
  15. 54
      lib/src/store.rs
  16. 20
      lib/tests/store.rs
  17. 47
      python/src/model.rs
  18. 25
      python/tests/test_model.py
  19. 5
      python/tests/test_store.py

@ -37,7 +37,7 @@ let store = Store::open("example.db")?;
// insertion
let ex = NamedNode::new("http://example.com")?;
let quad = Quad::new(ex.clone(), ex.clone(), ex.clone(), None);
let quad = Quad::new(ex.clone(), ex.clone(), ex.clone(), GraphName::DefaultGraph);
store.insert(&quad)?;
// quad filter

@ -1,5 +1,5 @@
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Throughput};
use oxigraph::model::{Dataset, Graph, NamedNode, Quad, Triple};
use oxigraph::model::{Dataset, Graph, GraphName, NamedNode, Quad, Triple};
use oxigraph::store::Store;
use rand::random;
use std::iter::FromIterator;
@ -80,7 +80,7 @@ fn create_quads(size: u64) -> Vec<Quad> {
"http://example.com/id/{}",
random::<u64>() % size
)),
None,
GraphName::DefaultGraph,
)
})
.collect()

@ -21,7 +21,7 @@
//!
//! // insertion
//! let ex = NamedNode::new("http://example.com")?;
//! let quad = Quad::new(ex.clone(), ex.clone(), ex.clone(), None);
//! let quad = Quad::new(ex.clone(), ex.clone(), ex.clone(), GraphName::DefaultGraph);
//! store.insert(&quad)?;
//!
//! // quad filter

@ -29,7 +29,6 @@ use crate::io::{
use crate::model::interning::*;
use crate::model::SubjectRef;
use crate::model::*;
use lasso::Rodeo;
use std::collections::hash_map::DefaultHasher;
use std::collections::BTreeSet;
use std::collections::{HashMap, HashSet};
@ -66,7 +65,8 @@ use std::{fmt, io};
/// ```
#[derive(Debug, Default)]
pub struct Dataset {
interner: Rodeo,
interner: Interner,
triples: HashMap<InternedTriple, Triple>,
gspo: BTreeSet<(
InternedGraphName,
InternedSubject,
@ -607,9 +607,13 @@ impl Dataset {
for (g, s, _, o) in &self.gspo {
if let InternedSubject::BlankNode(bnode) = s {
bnodes.insert(*bnode);
} else if let InternedSubject::Triple(triple) = s {
self.triple_blank_nodes(triple, &mut bnodes);
}
if let InternedTerm::BlankNode(bnode) = o {
bnodes.insert(*bnode);
} else if let InternedTerm::Triple(triple) = o {
self.triple_blank_nodes(triple, &mut bnodes);
}
if let InternedGraphName::BlankNode(bnode) = g {
bnodes.insert(*bnode);
@ -618,6 +622,19 @@ impl Dataset {
bnodes
}
fn triple_blank_nodes(&self, triple: &InternedTriple, bnodes: &mut HashSet<InternedBlankNode>) {
if let InternedSubject::BlankNode(bnode) = &triple.subject {
bnodes.insert(*bnode);
} else if let InternedSubject::Triple(t) = &triple.subject {
self.triple_blank_nodes(t, bnodes);
}
if let InternedTerm::BlankNode(bnode) = &triple.object {
bnodes.insert(*bnode);
} else if let InternedTerm::Triple(t) = &triple.object {
self.triple_blank_nodes(t, bnodes);
}
}
fn hash_bnodes(
&self,
mut hashes: HashMap<InternedBlankNode, u64>,
@ -688,7 +705,9 @@ impl Dataset {
bnodes_hash: &HashMap<InternedBlankNode, u64>,
) -> u64 {
if let InternedSubject::BlankNode(bnode) = node {
*bnodes_hash.get(bnode).unwrap()
bnodes_hash[bnode]
} else if let InternedSubject::Triple(triple) = node {
self.hash_triple(triple, bnodes_hash)
} else {
self.hash_tuple(node.decode_from(&self.interner))
}
@ -696,7 +715,9 @@ impl Dataset {
fn hash_term(&self, term: &InternedTerm, bnodes_hash: &HashMap<InternedBlankNode, u64>) -> u64 {
if let InternedTerm::BlankNode(bnode) = term {
*bnodes_hash.get(bnode).unwrap()
bnodes_hash[bnode]
} else if let InternedTerm::Triple(triple) = term {
self.hash_triple(triple, bnodes_hash)
} else {
self.hash_tuple(term.decode_from(&self.interner))
}
@ -708,12 +729,24 @@ impl Dataset {
bnodes_hash: &HashMap<InternedBlankNode, u64>,
) -> u64 {
if let InternedGraphName::BlankNode(bnode) = graph_name {
*bnodes_hash.get(bnode).unwrap()
bnodes_hash[bnode]
} else {
self.hash_tuple(graph_name.decode_from(&self.interner))
}
}
fn hash_triple(
&self,
triple: &InternedTriple,
bnodes_hash: &HashMap<InternedBlankNode, u64>,
) -> u64 {
self.hash_tuple((
self.hash_subject(&triple.subject, bnodes_hash),
self.hash_named_node(&triple.predicate),
self.hash_term(&triple.object, bnodes_hash),
))
}
fn hash_tuple(&self, v: impl Hash) -> u64 {
let mut hasher = DefaultHasher::new();
v.hash(&mut hasher);
@ -775,12 +808,22 @@ impl Dataset {
(
if let InternedSubject::BlankNode(bnode) = s {
InternedSubject::BlankNode(self.map_bnode(&bnode, hashes))
} else if let InternedSubject::Triple(triple) = s {
InternedSubject::Triple(Box::new(InternedTriple::encoded_into(
self.label_triple(&triple, hashes).as_ref(),
&mut self.interner,
)))
} else {
s
},
p,
if let InternedTerm::BlankNode(bnode) = o {
InternedTerm::BlankNode(self.map_bnode(&bnode, hashes))
} else if let InternedTerm::Triple(triple) = o {
InternedTerm::Triple(Box::new(InternedTriple::encoded_into(
self.label_triple(&triple, hashes).as_ref(),
&mut self.interner,
)))
} else {
o
},
@ -796,16 +839,48 @@ impl Dataset {
quads
}
fn label_triple(
&mut self,
triple: &InternedTriple,
hashes: &HashMap<InternedBlankNode, u64>,
) -> Triple {
Triple {
subject: if let InternedSubject::BlankNode(bnode) = &triple.subject {
self.gen_bnode(bnode, hashes).into()
} else if let InternedSubject::Triple(t) = &triple.subject {
self.label_triple(t, hashes).into()
} else {
triple.subject.decode_from(&self.interner).into_owned()
},
predicate: triple.predicate.decode_from(&self.interner).into_owned(),
object: if let InternedTerm::BlankNode(bnode) = &triple.object {
self.gen_bnode(bnode, hashes).into()
} else if let InternedTerm::Triple(t) = &triple.object {
self.label_triple(t, hashes).into()
} else {
triple.object.decode_from(&self.interner).into_owned()
},
}
}
fn map_bnode(
&mut self,
old_bnode: &InternedBlankNode,
hashes: &HashMap<InternedBlankNode, u64>,
) -> InternedBlankNode {
InternedBlankNode::encoded_into(
BlankNode::new_from_unique_id(*hashes.get(old_bnode).unwrap()).as_ref(),
self.gen_bnode(old_bnode, hashes).as_ref(),
&mut self.interner,
)
}
fn gen_bnode(
&self,
old_bnode: &InternedBlankNode,
hashes: &HashMap<InternedBlankNode, u64>,
) -> BlankNode {
BlankNode::new_from_unique_id(hashes[old_bnode])
}
}
impl PartialEq for Dataset {
@ -1151,10 +1226,13 @@ impl<'a> GraphView<'a> {
/// Checks if the graph contains the given triple
pub fn contains<'b>(&self, triple: impl Into<TripleRef<'b>>) -> bool {
if let Some((s, p, o)) = self.encoded_triple(triple.into()) {
self.dataset
.gspo
.contains(&(self.graph_name.clone(), s, p, o))
if let Some(triple) = self.encoded_triple(triple.into()) {
self.dataset.gspo.contains(&(
self.graph_name.clone(),
triple.subject,
triple.predicate,
triple.object,
))
} else {
false
}
@ -1195,15 +1273,12 @@ impl<'a> GraphView<'a> {
writer.finish()
}
fn encoded_triple(
&self,
triple: TripleRef<'_>,
) -> Option<(InternedSubject, InternedNamedNode, InternedTerm)> {
Some((
self.dataset.encoded_subject(triple.subject)?,
self.dataset.encoded_named_node(triple.predicate)?,
self.dataset.encoded_term(triple.object)?,
))
fn encoded_triple(&self, triple: TripleRef<'_>) -> Option<InternedTriple> {
Some(InternedTriple {
subject: self.dataset.encoded_subject(triple.subject)?,
predicate: self.dataset.encoded_named_node(triple.predicate)?,
object: self.dataset.encoded_term(triple.object)?,
})
}
}
@ -1274,16 +1349,24 @@ impl<'a> GraphViewMut<'a> {
/// Adds a triple to the graph
pub fn insert<'b>(&mut self, triple: impl Into<TripleRef<'b>>) -> bool {
let (s, p, o) = self.encode_triple(triple.into());
self.dataset
.insert_encoded((s, p, o, self.graph_name.clone()))
let triple = self.encode_triple(triple.into());
self.dataset.insert_encoded((
triple.subject,
triple.predicate,
triple.object,
self.graph_name.clone(),
))
}
/// Removes a concrete triple from the graph
pub fn remove<'b>(&mut self, triple: impl Into<TripleRef<'b>>) -> bool {
if let Some((s, p, o)) = self.read().encoded_triple(triple.into()) {
self.dataset
.remove_encoded((s, p, o, self.graph_name.clone()))
if let Some(triple) = self.read().encoded_triple(triple.into()) {
self.dataset.remove_encoded((
triple.subject,
triple.predicate,
triple.object,
self.graph_name.clone(),
))
} else {
false
}
@ -1332,15 +1415,15 @@ impl<'a> GraphViewMut<'a> {
Ok(())
}
fn encode_triple(
&mut self,
triple: TripleRef<'_>,
) -> (InternedSubject, InternedNamedNode, InternedTerm) {
(
InternedSubject::encoded_into(triple.subject, &mut self.dataset.interner),
InternedNamedNode::encoded_into(triple.predicate, &mut self.dataset.interner),
InternedTerm::encoded_into(triple.object, &mut self.dataset.interner),
)
fn encode_triple(&mut self, triple: TripleRef<'_>) -> InternedTriple {
InternedTriple {
subject: InternedSubject::encoded_into(triple.subject, &mut self.dataset.interner),
predicate: InternedNamedNode::encoded_into(
triple.predicate,
&mut self.dataset.interner,
),
object: InternedTerm::encoded_into(triple.object, &mut self.dataset.interner),
}
}
/// Returns all the triples contained by the graph

@ -2,28 +2,35 @@
use crate::model::*;
use lasso::{Key, Rodeo, Spur};
use std::collections::HashMap;
use std::convert::TryInto;
#[derive(Debug, Default)]
pub struct Interner {
strings: Rodeo,
triples: HashMap<InternedTriple, Triple>,
}
#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Copy, Hash)]
pub struct InternedNamedNode {
id: Spur,
}
impl InternedNamedNode {
pub fn encoded_into(named_node: NamedNodeRef<'_>, interner: &mut Rodeo) -> Self {
pub fn encoded_into(named_node: NamedNodeRef<'_>, interner: &mut Interner) -> Self {
Self {
id: interner.get_or_intern(named_node.as_str()),
id: interner.strings.get_or_intern(named_node.as_str()),
}
}
pub fn encoded_from(named_node: NamedNodeRef<'_>, interner: &Rodeo) -> Option<Self> {
pub fn encoded_from(named_node: NamedNodeRef<'_>, interner: &Interner) -> Option<Self> {
Some(Self {
id: interner.get(named_node.as_str())?,
id: interner.strings.get(named_node.as_str())?,
})
}
pub fn decode_from<'a>(&self, interner: &'a Rodeo) -> NamedNodeRef<'a> {
NamedNodeRef::new_unchecked(interner.resolve(&self.id))
pub fn decode_from<'a>(&self, interner: &'a Interner) -> NamedNodeRef<'a> {
NamedNodeRef::new_unchecked(interner.strings.resolve(&self.id))
}
pub fn first() -> Self {
@ -49,20 +56,20 @@ pub struct InternedBlankNode {
}
impl InternedBlankNode {
pub fn encoded_into(blank_node: BlankNodeRef<'_>, interner: &mut Rodeo) -> Self {
pub fn encoded_into(blank_node: BlankNodeRef<'_>, interner: &mut Interner) -> Self {
Self {
id: interner.get_or_intern(blank_node.as_str()),
id: interner.strings.get_or_intern(blank_node.as_str()),
}
}
pub fn encoded_from(blank_node: BlankNodeRef<'_>, interner: &Rodeo) -> Option<Self> {
pub fn encoded_from(blank_node: BlankNodeRef<'_>, interner: &Interner) -> Option<Self> {
Some(Self {
id: interner.get(blank_node.as_str())?,
id: interner.strings.get(blank_node.as_str())?,
})
}
pub fn decode_from<'a>(&self, interner: &'a Rodeo) -> BlankNodeRef<'a> {
BlankNodeRef::new_unchecked(interner.resolve(&self.id))
pub fn decode_from<'a>(&self, interner: &'a Interner) -> BlankNodeRef<'a> {
BlankNodeRef::new_unchecked(interner.strings.resolve(&self.id))
}
pub fn next(&self) -> Self {
@ -88,13 +95,13 @@ pub enum InternedLiteral {
}
impl InternedLiteral {
pub fn encoded_into(literal: LiteralRef<'_>, interner: &mut Rodeo) -> Self {
let value_id = interner.get_or_intern(literal.value());
pub fn encoded_into(literal: LiteralRef<'_>, interner: &mut Interner) -> Self {
let value_id = interner.strings.get_or_intern(literal.value());
if literal.is_plain() {
if let Some(language) = literal.language() {
Self::LanguageTaggedString {
value_id,
language_id: interner.get_or_intern(language),
language_id: interner.strings.get_or_intern(language),
}
} else {
Self::String { value_id }
@ -107,13 +114,13 @@ impl InternedLiteral {
}
}
pub fn encoded_from(literal: LiteralRef<'_>, interner: &Rodeo) -> Option<Self> {
let value_id = interner.get(literal.value())?;
pub fn encoded_from(literal: LiteralRef<'_>, interner: &Interner) -> Option<Self> {
let value_id = interner.strings.get(literal.value())?;
Some(if literal.is_plain() {
if let Some(language) = literal.language() {
Self::LanguageTaggedString {
value_id,
language_id: interner.get(language)?,
language_id: interner.strings.get(language)?,
}
} else {
Self::String { value_id }
@ -126,20 +133,20 @@ impl InternedLiteral {
})
}
pub fn decode_from<'a>(&self, interner: &'a Rodeo) -> LiteralRef<'a> {
pub fn decode_from<'a>(&self, interner: &'a Interner) -> LiteralRef<'a> {
match self {
InternedLiteral::String { value_id } => {
LiteralRef::new_simple_literal(interner.resolve(value_id))
LiteralRef::new_simple_literal(interner.strings.resolve(value_id))
}
InternedLiteral::LanguageTaggedString {
value_id,
language_id,
} => LiteralRef::new_language_tagged_literal_unchecked(
interner.resolve(value_id),
interner.resolve(language_id),
interner.strings.resolve(value_id),
interner.strings.resolve(language_id),
),
InternedLiteral::TypedLiteral { value_id, datatype } => LiteralRef::new_typed_literal(
interner.resolve(value_id),
interner.strings.resolve(value_id),
datatype.decode_from(interner),
),
}
@ -169,10 +176,11 @@ impl InternedLiteral {
pub enum InternedSubject {
NamedNode(InternedNamedNode),
BlankNode(InternedBlankNode),
Triple(Box<InternedTriple>),
}
impl InternedSubject {
pub fn encoded_into(node: SubjectRef<'_>, interner: &mut Rodeo) -> Self {
pub fn encoded_into(node: SubjectRef<'_>, interner: &mut Interner) -> Self {
match node {
SubjectRef::NamedNode(node) => {
Self::NamedNode(InternedNamedNode::encoded_into(node, interner))
@ -180,10 +188,14 @@ impl InternedSubject {
SubjectRef::BlankNode(node) => {
Self::BlankNode(InternedBlankNode::encoded_into(node, interner))
}
SubjectRef::Triple(triple) => Self::Triple(Box::new(InternedTriple::encoded_into(
triple.as_ref(),
interner,
))),
}
}
pub fn encoded_from(node: SubjectRef<'_>, interner: &Rodeo) -> Option<Self> {
pub fn encoded_from(node: SubjectRef<'_>, interner: &Interner) -> Option<Self> {
Some(match node {
SubjectRef::NamedNode(node) => {
Self::NamedNode(InternedNamedNode::encoded_from(node, interner)?)
@ -191,13 +203,18 @@ impl InternedSubject {
SubjectRef::BlankNode(node) => {
Self::BlankNode(InternedBlankNode::encoded_from(node, interner)?)
}
SubjectRef::Triple(triple) => Self::Triple(Box::new(InternedTriple::encoded_from(
triple.as_ref(),
interner,
)?)),
})
}
pub fn decode_from<'a>(&self, interner: &'a Rodeo) -> SubjectRef<'a> {
pub fn decode_from<'a>(&self, interner: &'a Interner) -> SubjectRef<'a> {
match self {
Self::NamedNode(node) => SubjectRef::NamedNode(node.decode_from(interner)),
Self::BlankNode(node) => SubjectRef::BlankNode(node.decode_from(interner)),
Self::Triple(triple) => SubjectRef::Triple(&interner.triples[triple.as_ref()]),
}
}
@ -209,6 +226,7 @@ impl InternedSubject {
match self {
Self::NamedNode(node) => Self::NamedNode(node.next()),
Self::BlankNode(node) => Self::BlankNode(node.next()),
Self::Triple(triple) => Self::Triple(Box::new(triple.next())),
}
}
@ -225,7 +243,7 @@ pub enum InternedGraphName {
}
impl InternedGraphName {
pub fn encoded_into(node: GraphNameRef<'_>, interner: &mut Rodeo) -> Self {
pub fn encoded_into(node: GraphNameRef<'_>, interner: &mut Interner) -> Self {
match node {
GraphNameRef::DefaultGraph => Self::DefaultGraph,
GraphNameRef::NamedNode(node) => {
@ -237,7 +255,7 @@ impl InternedGraphName {
}
}
pub fn encoded_from(node: GraphNameRef<'_>, interner: &Rodeo) -> Option<Self> {
pub fn encoded_from(node: GraphNameRef<'_>, interner: &Interner) -> Option<Self> {
Some(match node {
GraphNameRef::DefaultGraph => Self::DefaultGraph,
GraphNameRef::NamedNode(node) => {
@ -249,7 +267,7 @@ impl InternedGraphName {
})
}
pub fn decode_from<'a>(&self, interner: &'a Rodeo) -> GraphNameRef<'a> {
pub fn decode_from<'a>(&self, interner: &'a Interner) -> GraphNameRef<'a> {
match self {
Self::DefaultGraph => GraphNameRef::DefaultGraph,
Self::NamedNode(node) => GraphNameRef::NamedNode(node.decode_from(interner)),
@ -279,10 +297,11 @@ pub enum InternedTerm {
NamedNode(InternedNamedNode),
BlankNode(InternedBlankNode),
Literal(InternedLiteral),
Triple(Box<InternedTriple>),
}
impl InternedTerm {
pub fn encoded_into(term: TermRef<'_>, interner: &mut Rodeo) -> Self {
pub fn encoded_into(term: TermRef<'_>, interner: &mut Interner) -> Self {
match term {
TermRef::NamedNode(term) => {
Self::NamedNode(InternedNamedNode::encoded_into(term, interner))
@ -291,10 +310,14 @@ impl InternedTerm {
Self::BlankNode(InternedBlankNode::encoded_into(term, interner))
}
TermRef::Literal(term) => Self::Literal(InternedLiteral::encoded_into(term, interner)),
TermRef::Triple(triple) => Self::Triple(Box::new(InternedTriple::encoded_into(
triple.as_ref(),
interner,
))),
}
}
pub fn encoded_from(term: TermRef<'_>, interner: &Rodeo) -> Option<Self> {
pub fn encoded_from(term: TermRef<'_>, interner: &Interner) -> Option<Self> {
Some(match term {
TermRef::NamedNode(term) => {
Self::NamedNode(InternedNamedNode::encoded_from(term, interner)?)
@ -303,14 +326,19 @@ impl InternedTerm {
Self::BlankNode(InternedBlankNode::encoded_from(term, interner)?)
}
TermRef::Literal(term) => Self::Literal(InternedLiteral::encoded_from(term, interner)?),
TermRef::Triple(triple) => Self::Triple(Box::new(InternedTriple::encoded_from(
triple.as_ref(),
interner,
)?)),
})
}
pub fn decode_from<'a>(&self, interner: &'a Rodeo) -> TermRef<'a> {
pub fn decode_from<'a>(&self, interner: &'a Interner) -> TermRef<'a> {
match self {
Self::NamedNode(term) => TermRef::NamedNode(term.decode_from(interner)),
Self::BlankNode(term) => TermRef::BlankNode(term.decode_from(interner)),
Self::Literal(term) => TermRef::Literal(term.decode_from(interner)),
Self::Triple(triple) => TermRef::Triple(&interner.triples[triple.as_ref()]),
}
}
@ -323,6 +351,7 @@ impl InternedTerm {
Self::NamedNode(node) => Self::NamedNode(node.next()),
Self::BlankNode(node) => Self::BlankNode(node.next()),
Self::Literal(node) => Self::Literal(node.next()),
Self::Triple(triple) => Self::Triple(Box::new(triple.next())),
}
}
@ -331,6 +360,48 @@ impl InternedTerm {
}
}
#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Hash)]
pub struct InternedTriple {
pub subject: InternedSubject,
pub predicate: InternedNamedNode,
pub object: InternedTerm,
}
impl InternedTriple {
pub fn encoded_into(triple: TripleRef<'_>, interner: &mut Interner) -> Self {
let interned_triple = Self {
subject: InternedSubject::encoded_into(triple.subject, interner),
predicate: InternedNamedNode::encoded_into(triple.predicate, interner),
object: InternedTerm::encoded_into(triple.object, interner),
};
interner
.triples
.insert(interned_triple.clone(), triple.into_owned());
interned_triple
}
pub fn encoded_from(triple: TripleRef<'_>, interner: &Interner) -> Option<Self> {
let interned_triple = Self {
subject: InternedSubject::encoded_from(triple.subject, interner)?,
predicate: InternedNamedNode::encoded_from(triple.predicate, interner)?,
object: InternedTerm::encoded_from(triple.object, interner)?,
};
if interner.triples.contains_key(&interned_triple) {
Some(interned_triple)
} else {
None
}
}
pub fn next(&self) -> Self {
Self {
subject: self.subject.clone(),
predicate: self.predicate,
object: self.object.next(),
}
}
}
fn fist_spur() -> Spur {
Spur::try_from_usize(0).unwrap()
}

@ -191,26 +191,26 @@ impl<'a> From<GraphNameRef<'a>> for Option<TermRef<'a>> {
impl TTerm for Subject {
fn kind(&self) -> TermKind {
use Subject::*;
match self {
NamedNode(_) => TermKind::Iri,
BlankNode(_) => TermKind::BlankNode,
Self::NamedNode(_) => TermKind::Iri,
Self::BlankNode(_) => TermKind::BlankNode,
Self::Triple(_) => panic!("RDF-star is not supported yet by Sophia"),
}
}
fn value_raw(&self) -> RawValue<'_> {
use Subject::*;
match self {
NamedNode(n) => n.value_raw(),
BlankNode(n) => n.value_raw(),
Self::NamedNode(n) => n.value_raw(),
Self::BlankNode(n) => n.value_raw(),
Self::Triple(_) => panic!("RDF-star is not supported yet by Sophia"),
}
}
fn as_dyn(&self) -> &dyn TTerm {
use Subject::*;
match self {
NamedNode(n) => n.as_dyn(),
BlankNode(n) => n.as_dyn(),
Self::NamedNode(n) => n.as_dyn(),
Self::BlankNode(n) => n.as_dyn(),
Self::Triple(_) => panic!("RDF-star is not supported yet by Sophia"),
}
}
}
@ -232,71 +232,71 @@ impl TryCopyTerm for Subject {
impl<'a> TTerm for SubjectRef<'a> {
fn kind(&self) -> TermKind {
use SubjectRef::*;
match self {
NamedNode(_) => TermKind::Iri,
BlankNode(_) => TermKind::BlankNode,
Self::NamedNode(_) => TermKind::Iri,
Self::BlankNode(_) => TermKind::BlankNode,
Self::Triple(_) => panic!("RDF-star is not supported yet by Sophia"),
}
}
fn value_raw(&self) -> RawValue<'_> {
use SubjectRef::*;
match self {
NamedNode(n) => n.value_raw(),
BlankNode(n) => n.value_raw(),
Self::NamedNode(n) => n.value_raw(),
Self::BlankNode(n) => n.value_raw(),
Self::Triple(_) => panic!("RDF-star is not supported yet by Sophia"),
}
}
fn as_dyn(&self) -> &dyn TTerm {
use SubjectRef::*;
match self {
NamedNode(n) => n.as_dyn(),
BlankNode(n) => n.as_dyn(),
Self::NamedNode(n) => n.as_dyn(),
Self::BlankNode(n) => n.as_dyn(),
Self::Triple(_) => panic!("RDF-star is not supported yet by Sophia"),
}
}
}
impl TTerm for Term {
fn kind(&self) -> TermKind {
use Term::*;
match self {
NamedNode(_) => TermKind::Iri,
BlankNode(_) => TermKind::BlankNode,
Literal(_) => TermKind::Literal,
Self::NamedNode(_) => TermKind::Iri,
Self::BlankNode(_) => TermKind::BlankNode,
Self::Literal(_) => TermKind::Literal,
Self::Triple(_) => panic!("RDF-star is not supported yet by Sophia"),
}
}
fn value_raw(&self) -> RawValue<'_> {
use Term::*;
match self {
NamedNode(n) => n.value_raw(),
BlankNode(n) => n.value_raw(),
Literal(l) => l.value_raw(),
Self::NamedNode(n) => n.value_raw(),
Self::BlankNode(n) => n.value_raw(),
Self::Literal(l) => l.value_raw(),
Self::Triple(_) => panic!("RDF-star is not supported yet by Sophia"),
}
}
fn datatype(&self) -> Option<SimpleIri<'_>> {
use Term::*;
match self {
Literal(l) => TTerm::datatype(l),
_ => None,
if let Self::Literal(l) = self {
TTerm::datatype(l)
} else {
None
}
}
fn language(&self) -> Option<&str> {
use Term::*;
match self {
Literal(l) => TTerm::language(l),
_ => None,
if let Self::Literal(l) = self {
TTerm::language(l)
} else {
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(),
Self::NamedNode(n) => n.as_dyn(),
Self::BlankNode(n) => n.as_dyn(),
Self::Literal(l) => l.as_dyn(),
Self::Triple(_) => panic!("RDF-star is not supported yet by Sophia"),
}
}
}
@ -319,45 +319,45 @@ impl TryCopyTerm for Term {
impl<'a> TTerm for TermRef<'a> {
fn kind(&self) -> TermKind {
use TermRef::*;
match self {
NamedNode(_) => TermKind::Iri,
BlankNode(_) => TermKind::BlankNode,
Literal(_) => TermKind::Literal,
Self::NamedNode(_) => TermKind::Iri,
Self::BlankNode(_) => TermKind::BlankNode,
Self::Literal(_) => TermKind::Literal,
Self::Triple(_) => panic!("RDF-star is not supported yet by Sophia"),
}
}
fn value_raw(&self) -> RawValue<'_> {
use TermRef::*;
match self {
NamedNode(n) => n.value_raw(),
BlankNode(n) => n.value_raw(),
Literal(l) => l.value_raw(),
Self::NamedNode(n) => n.value_raw(),
Self::BlankNode(n) => n.value_raw(),
Self::Literal(l) => l.value_raw(),
Self::Triple(_) => panic!("RDF-star is not supported yet by Sophia"),
}
}
fn datatype(&self) -> Option<SimpleIri<'_>> {
use TermRef::*;
match self {
Literal(l) => TTerm::datatype(l),
_ => None,
if let Self::Literal(l) = self {
TTerm::datatype(l)
} else {
None
}
}
fn language(&self) -> Option<&str> {
use TermRef::*;
match self {
Literal(l) => TTerm::language(l),
_ => None,
if let Self::Literal(l) = self {
TTerm::language(l)
} else {
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(),
Self::NamedNode(n) => n.as_dyn(),
Self::BlankNode(n) => n.as_dyn(),
Self::Literal(l) => l.as_dyn(),
Self::Triple(_) => panic!("RDF-star is not supported yet by Sophia"),
}
}
}

@ -4,12 +4,14 @@ use crate::model::named_node::NamedNode;
use crate::model::{BlankNodeRef, LiteralRef, NamedNodeRef};
use rio_api::model as rio;
use std::fmt;
use std::sync::Arc;
/// The owned union of [IRIs](https://www.w3.org/TR/rdf11-concepts/#dfn-iri) and [blank nodes](https://www.w3.org/TR/rdf11-concepts/#dfn-blank-node).
/// The owned union of [IRIs](https://www.w3.org/TR/rdf11-concepts/#dfn-iri), [blank nodes](https://www.w3.org/TR/rdf11-concepts/#dfn-blank-node) and [triples](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-triple).
#[derive(Eq, PartialEq, Debug, Clone, Hash)]
pub enum Subject {
NamedNode(NamedNode),
BlankNode(BlankNode),
Triple(Arc<Triple>),
}
impl Subject {
@ -23,11 +25,17 @@ impl Subject {
self.as_ref().is_blank_node()
}
#[inline]
pub fn is_triple(&self) -> bool {
self.as_ref().is_triple()
}
#[inline]
pub fn as_ref(&self) -> SubjectRef<'_> {
match self {
Self::NamedNode(node) => SubjectRef::NamedNode(node.as_ref()),
Self::BlankNode(node) => SubjectRef::BlankNode(node.as_ref()),
Self::Triple(triple) => SubjectRef::Triple(triple),
}
}
}
@ -67,28 +75,42 @@ impl From<BlankNodeRef<'_>> for Subject {
}
}
/// The borrowed union of [IRIs](https://www.w3.org/TR/rdf11-concepts/#dfn-iri) and [blank nodes](https://www.w3.org/TR/rdf11-concepts/#dfn-blank-node).
impl From<Triple> for Subject {
#[inline]
fn from(node: Triple) -> Self {
Self::Triple(Arc::new(node))
}
}
impl From<TripleRef<'_>> for Subject {
#[inline]
fn from(node: TripleRef<'_>) -> Self {
node.into_owned().into()
}
}
/// The borrowed union of [IRIs](https://www.w3.org/TR/rdf11-concepts/#dfn-iri), [blank nodes](https://www.w3.org/TR/rdf11-concepts/#dfn-blank-node) and [triples](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-triple).
#[derive(Eq, PartialEq, Debug, Clone, Copy, Hash)]
pub enum SubjectRef<'a> {
NamedNode(NamedNodeRef<'a>),
BlankNode(BlankNodeRef<'a>),
Triple(&'a Triple),
}
impl<'a> SubjectRef<'a> {
#[inline]
pub fn is_named_node(&self) -> bool {
match self {
Self::NamedNode(_) => true,
Self::BlankNode(_) => false,
}
matches!(self, Self::NamedNode(_))
}
#[inline]
pub fn is_blank_node(&self) -> bool {
match self {
Self::NamedNode(_) => false,
Self::BlankNode(_) => true,
matches!(self, Self::BlankNode(_))
}
#[inline]
pub fn is_triple(&self) -> bool {
matches!(self, Self::Triple(_))
}
#[inline]
@ -96,6 +118,7 @@ impl<'a> SubjectRef<'a> {
match self {
Self::NamedNode(node) => Subject::NamedNode(node.into_owned()),
Self::BlankNode(node) => Subject::BlankNode(node.into_owned()),
Self::Triple(triple) => Subject::Triple(Arc::new(triple.clone())),
}
}
}
@ -106,6 +129,11 @@ impl fmt::Display for SubjectRef<'_> {
match self {
Self::NamedNode(node) => node.fmt(f),
Self::BlankNode(node) => node.fmt(f),
Self::Triple(triple) => write!(
f,
"<< {} {} {} >>",
triple.subject, triple.predicate, triple.object
),
}
}
}
@ -138,6 +166,13 @@ impl<'a> From<&'a BlankNode> for SubjectRef<'a> {
}
}
impl<'a> From<&'a Triple> for SubjectRef<'a> {
#[inline]
fn from(node: &'a Triple) -> Self {
Self::Triple(node)
}
}
impl<'a> From<&'a Subject> for SubjectRef<'a> {
#[inline]
fn from(node: &'a Subject) -> Self {
@ -152,23 +187,26 @@ impl<'a> From<SubjectRef<'a>> for Subject {
}
}
#[allow(clippy::unimplemented, clippy::fallible_impl_from)]
impl<'a> From<SubjectRef<'a>> for rio::NamedOrBlankNode<'a> {
#[inline]
fn from(node: SubjectRef<'a>) -> Self {
match node {
SubjectRef::NamedNode(node) => rio::NamedNode::from(node).into(),
SubjectRef::BlankNode(node) => rio::BlankNode::from(node).into(),
SubjectRef::Triple(_) => unimplemented!("Rio library does not support RDF* yet"),
}
}
}
/// An owned RDF [term](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-term)
/// It is the union of [IRIs](https://www.w3.org/TR/rdf11-concepts/#dfn-iri), [blank nodes](https://www.w3.org/TR/rdf11-concepts/#dfn-blank-node) and [literals](https://www.w3.org/TR/rdf11-concepts/#dfn-literal).
/// It is the union of [IRIs](https://www.w3.org/TR/rdf11-concepts/#dfn-iri), [blank nodes](https://www.w3.org/TR/rdf11-concepts/#dfn-blank-node), [literals](https://www.w3.org/TR/rdf11-concepts/#dfn-literal) and [triples](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-triple).
#[derive(Eq, PartialEq, Debug, Clone, Hash)]
pub enum Term {
NamedNode(NamedNode),
BlankNode(BlankNode),
Literal(Literal),
Triple(Arc<Triple>),
}
impl Term {
@ -187,12 +225,18 @@ impl Term {
self.as_ref().is_literal()
}
#[inline]
pub fn is_triple(&self) -> bool {
self.as_ref().is_triple()
}
#[inline]
pub fn as_ref(&self) -> TermRef<'_> {
match self {
Self::NamedNode(node) => TermRef::NamedNode(node.as_ref()),
Self::BlankNode(node) => TermRef::BlankNode(node.as_ref()),
Self::Literal(literal) => TermRef::Literal(literal.as_ref()),
Self::Triple(triple) => TermRef::Triple(triple),
}
}
}
@ -245,6 +289,19 @@ impl From<LiteralRef<'_>> for Term {
literal.into_owned().into()
}
}
impl From<Triple> for Term {
#[inline]
fn from(triple: Triple) -> Self {
Self::Triple(Arc::new(triple))
}
}
impl From<TripleRef<'_>> for Term {
#[inline]
fn from(triple: TripleRef<'_>) -> Self {
triple.into_owned().into()
}
}
impl From<Subject> for Term {
#[inline]
@ -252,6 +309,7 @@ impl From<Subject> for Term {
match node {
Subject::NamedNode(node) => node.into(),
Subject::BlankNode(node) => node.into(),
Subject::Triple(triple) => Self::Triple(triple),
}
}
}
@ -264,12 +322,13 @@ impl From<SubjectRef<'_>> for Term {
}
/// A borrowed RDF [term](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-term)
/// It is the union of [IRIs](https://www.w3.org/TR/rdf11-concepts/#dfn-iri), [blank nodes](https://www.w3.org/TR/rdf11-concepts/#dfn-blank-node) and [literals](https://www.w3.org/TR/rdf11-concepts/#dfn-literal).
/// It is the union of [IRIs](https://www.w3.org/TR/rdf11-concepts/#dfn-iri), [blank nodes](https://www.w3.org/TR/rdf11-concepts/#dfn-blank-node), [literals](https://www.w3.org/TR/rdf11-concepts/#dfn-literal) and [triples](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-triple).
#[derive(Eq, PartialEq, Debug, Clone, Copy, Hash)]
pub enum TermRef<'a> {
NamedNode(NamedNodeRef<'a>),
BlankNode(BlankNodeRef<'a>),
Literal(LiteralRef<'a>),
Triple(&'a Triple),
}
impl<'a> TermRef<'a> {
@ -288,12 +347,18 @@ impl<'a> TermRef<'a> {
matches!(self, Self::Literal(_))
}
#[inline]
pub fn is_triple(&self) -> bool {
matches!(self, Self::Triple(_))
}
#[inline]
pub fn into_owned(self) -> Term {
match self {
Self::NamedNode(node) => Term::NamedNode(node.into_owned()),
Self::BlankNode(node) => Term::BlankNode(node.into_owned()),
Self::Literal(literal) => Term::Literal(literal.into_owned()),
Self::Triple(triple) => Term::Triple(Arc::new(triple.clone())),
}
}
}
@ -304,7 +369,14 @@ impl fmt::Display for TermRef<'_> {
match self {
Self::NamedNode(node) => node.fmt(f),
Self::BlankNode(node) => node.fmt(f),
Self::Literal(node) => node.fmt(f),
Self::Literal(literal) => literal.fmt(f),
Self::Triple(triple) => {
write!(
f,
"<< {} {} {} >>",
triple.subject, triple.predicate, triple.object
)
}
}
}
}
@ -351,12 +423,20 @@ impl<'a> From<&'a Literal> for TermRef<'a> {
}
}
impl<'a> From<&'a Triple> for TermRef<'a> {
#[inline]
fn from(node: &'a Triple) -> Self {
Self::Triple(node)
}
}
impl<'a> From<SubjectRef<'a>> for TermRef<'a> {
#[inline]
fn from(node: SubjectRef<'a>) -> Self {
match node {
SubjectRef::NamedNode(node) => node.into(),
SubjectRef::BlankNode(node) => node.into(),
SubjectRef::Triple(triple) => triple.into(),
}
}
}
@ -382,6 +462,7 @@ impl<'a> From<TermRef<'a>> for Term {
}
}
#[allow(clippy::unimplemented, clippy::fallible_impl_from)]
impl<'a> From<TermRef<'a>> for rio::Term<'a> {
#[inline]
fn from(node: TermRef<'a>) -> Self {
@ -389,6 +470,7 @@ impl<'a> From<TermRef<'a>> for rio::Term<'a> {
TermRef::NamedNode(node) => rio::NamedNode::from(node).into(),
TermRef::BlankNode(node) => rio::BlankNode::from(node).into(),
TermRef::Literal(node) => rio::Literal::from(node).into(),
TermRef::Triple(_) => unimplemented!("Rio library does not support RDF* yet"),
}
}
}
@ -600,45 +682,6 @@ impl From<BlankNodeRef<'_>> for GraphName {
}
}
impl From<Subject> for GraphName {
#[inline]
fn from(node: Subject) -> Self {
match node {
Subject::NamedNode(node) => node.into(),
Subject::BlankNode(node) => node.into(),
}
}
}
impl From<SubjectRef<'_>> for GraphName {
#[inline]
fn from(node: SubjectRef<'_>) -> Self {
node.into_owned().into()
}
}
impl From<Option<Subject>> for GraphName {
#[inline]
fn from(name: Option<Subject>) -> Self {
if let Some(node) = name {
node.into()
} else {
GraphName::DefaultGraph
}
}
}
impl From<GraphName> for Option<Subject> {
#[inline]
fn from(name: GraphName) -> Self {
match name {
GraphName::NamedNode(node) => Some(node.into()),
GraphName::BlankNode(node) => Some(node.into()),
GraphName::DefaultGraph => None,
}
}
}
/// A possible borrowed graph name.
/// It is the union of [IRIs](https://www.w3.org/TR/rdf11-concepts/#dfn-iri), [blank nodes](https://www.w3.org/TR/rdf11-concepts/#dfn-blank-node), and the [default graph name](https://www.w3.org/TR/rdf11-concepts/#dfn-default-graph).
#[derive(Eq, PartialEq, Debug, Clone, Copy, Hash)]
@ -713,23 +756,6 @@ impl<'a> From<&'a BlankNode> for GraphNameRef<'a> {
}
}
impl<'a> From<SubjectRef<'a>> for GraphNameRef<'a> {
#[inline]
fn from(node: SubjectRef<'a>) -> Self {
match node {
SubjectRef::NamedNode(node) => node.into(),
SubjectRef::BlankNode(node) => node.into(),
}
}
}
impl<'a> From<&'a Subject> for GraphNameRef<'a> {
#[inline]
fn from(node: &'a Subject) -> Self {
node.as_ref().into()
}
}
impl<'a> From<&'a GraphName> for GraphNameRef<'a> {
#[inline]
fn from(node: &'a GraphName) -> Self {
@ -744,28 +770,6 @@ impl<'a> From<GraphNameRef<'a>> for GraphName {
}
}
impl<'a> From<Option<SubjectRef<'a>>> for GraphNameRef<'a> {
#[inline]
fn from(name: Option<SubjectRef<'a>>) -> Self {
if let Some(node) = name {
node.into()
} else {
GraphNameRef::DefaultGraph
}
}
}
impl<'a> From<GraphNameRef<'a>> for Option<SubjectRef<'a>> {
#[inline]
fn from(name: GraphNameRef<'a>) -> Self {
match name {
GraphNameRef::NamedNode(node) => Some(node.into()),
GraphNameRef::BlankNode(node) => Some(node.into()),
GraphNameRef::DefaultGraph => None,
}
}
}
impl<'a> From<GraphNameRef<'a>> for Option<rio::NamedOrBlankNode<'a>> {
#[inline]
fn from(name: GraphNameRef<'a>) -> Self {

@ -59,18 +59,25 @@ pub fn write_csv_results(
Ok(())
}
fn write_csv_term<'a>(term: impl Into<TermRef<'a>>, mut sink: impl Write) -> io::Result<()> {
fn write_csv_term<'a>(term: impl Into<TermRef<'a>>, sink: &mut impl Write) -> io::Result<()> {
match term.into() {
TermRef::NamedNode(uri) => sink.write_all(uri.as_str().as_bytes()),
TermRef::BlankNode(bnode) => {
sink.write_all(b"_:")?;
sink.write_all(bnode.as_str().as_bytes())
}
TermRef::Literal(literal) => write_escaped_csv_string(literal.value(), &mut sink),
TermRef::Literal(literal) => write_escaped_csv_string(literal.value(), sink),
TermRef::Triple(triple) => {
write_csv_term(&triple.subject, sink)?;
sink.write_all(b" ")?;
write_csv_term(&triple.predicate, sink)?;
sink.write_all(b" ")?;
write_csv_term(&triple.object, sink)
}
}
}
fn write_escaped_csv_string(s: &str, mut sink: impl Write) -> io::Result<()> {
fn write_escaped_csv_string(s: &str, sink: &mut impl Write) -> io::Result<()> {
if s.bytes().any(|c| matches!(c, b'"' | b',' | b'\n' | b'\r')) {
sink.write_all(b"\"")?;
for c in s.bytes() {
@ -138,7 +145,7 @@ pub fn write_tsv_results(
Ok(())
}
fn write_tsv_term<'a>(term: impl Into<TermRef<'a>>, mut sink: impl Write) -> io::Result<()> {
fn write_tsv_term<'a>(term: impl Into<TermRef<'a>>, sink: &mut impl Write) -> io::Result<()> {
//TODO: full Turtle serialization
match term.into() {
TermRef::NamedNode(node) => write!(sink, "<{}>", node.as_str()),
@ -158,6 +165,16 @@ fn write_tsv_term<'a>(term: impl Into<TermRef<'a>>, mut sink: impl Write) -> io:
}
_ => sink.write_all(literal.to_string().as_bytes()),
},
TermRef::Triple(triple) => {
sink.write_all(b"<< ")?;
write_tsv_term(&triple.subject, sink)?;
sink.write_all(b" ")?;
write_tsv_term(&triple.predicate, sink)?;
sink.write_all(b" ")?;
write_tsv_term(&triple.object, sink)?;
sink.write_all(b" >>")?;
Ok(())
}
}
}

@ -1590,7 +1590,8 @@ impl SimpleEvaluator {
EncodedTerm::NamedNode { iri_id } => Some((*iri_id).into()),
EncodedTerm::NumericalBlankNode { .. }
| EncodedTerm::SmallBlankNode { .. }
| EncodedTerm::BigBlankNode { .. } => None,
| EncodedTerm::BigBlankNode { .. }
| EncodedTerm::Triple(_) => None,
EncodedTerm::SmallStringLiteral(value)
| EncodedTerm::SmallSmallLangStringLiteral { value, .. }
| EncodedTerm::SmallBigLangStringLiteral { value, .. }
@ -2005,6 +2006,17 @@ impl SimpleEvaluator {
_ if b.is_unknown_typed_literal() => None,
_ => Some(false),
},
EncodedTerm::Triple(a) => {
if let EncodedTerm::Triple(b) = b {
Some(
self.equals(&a.subject, &b.subject)?
&& self.equals(&a.predicate, &b.predicate)?
&& self.equals(&a.object, &b.object)?,
)
} else {
Some(false)
}
}
}
}
@ -2194,7 +2206,8 @@ impl SimpleEvaluator {
| EncodedTerm::SmallBlankNode { .. }
| EncodedTerm::BigBlankNode { .. }
| EncodedTerm::NumericalBlankNode { .. }
| EncodedTerm::DefaultGraph => None,
| EncodedTerm::DefaultGraph
| EncodedTerm::Triple(_) => None,
EncodedTerm::SmallStringLiteral(_) | EncodedTerm::BigStringLiteral { .. } => {
self.build_named_node(xsd::STRING.as_str())
}

@ -47,44 +47,59 @@ pub fn write_json_results(
sink.write_all(b",")?;
}
write_escaped_json_string(variable.as_str(), &mut sink)?;
match value {
Term::NamedNode(uri) => {
sink.write_all(b":{\"type\":\"uri\",\"value\":")?;
write_escaped_json_string(uri.as_str(), &mut sink)?;
sink.write_all(b":")?;
write_json_term(value.as_ref(), &mut sink)?;
}
sink.write_all(b"}")?;
}
sink.write_all(b"]}}")?;
Ok(())
}
QueryResults::Graph(_) => Err(invalid_input_error(
"Graphs could not be formatted to SPARQL query results XML format",
)
.into()),
}
}
fn write_json_term(term: TermRef<'_>, sink: &mut impl Write) -> Result<(), EvaluationError> {
match term {
TermRef::NamedNode(uri) => {
sink.write_all(b"{\"type\":\"uri\",\"value\":")?;
write_escaped_json_string(uri.as_str(), sink)?;
sink.write_all(b"}")?;
}
Term::BlankNode(bnode) => {
sink.write_all(b":{\"type\":\"bnode\",\"value\":")?;
write_escaped_json_string(bnode.as_str(), &mut sink)?;
TermRef::BlankNode(bnode) => {
sink.write_all(b"{\"type\":\"bnode\",\"value\":")?;
write_escaped_json_string(bnode.as_str(), sink)?;
sink.write_all(b"}")?;
}
Term::Literal(literal) => {
sink.write_all(b":{\"type\":\"literal\",\"value\":")?;
write_escaped_json_string(literal.value(), &mut sink)?;
TermRef::Literal(literal) => {
sink.write_all(b"{\"type\":\"literal\",\"value\":")?;
write_escaped_json_string(literal.value(), sink)?;
if let Some(language) = literal.language() {
sink.write_all(b",\"xml:lang\":")?;
write_escaped_json_string(language, &mut sink)?;
write_escaped_json_string(language, sink)?;
} else if !literal.is_plain() {
sink.write_all(b",\"datatype\":")?;
write_escaped_json_string(literal.datatype().as_str(), &mut sink)?;
write_escaped_json_string(literal.datatype().as_str(), sink)?;
}
sink.write_all(b"}")?;
}
TermRef::Triple(triple) => {
sink.write_all(b":{\"type\":\"triple\",\"value\":{\"subject\":")?;
write_json_term(triple.subject.as_ref().into(), sink)?;
sink.write_all(b":,\"predicate\":")?;
write_json_term(triple.predicate.as_ref().into(), sink)?;
sink.write_all(b":,\"object\":")?;
write_json_term(triple.object.as_ref(), sink)?;
sink.write_all(b"}}")?;
}
}
sink.write_all(b"}")?;
}
sink.write_all(b"]}}")?;
Ok(())
}
QueryResults::Graph(_) => Err(invalid_input_error(
"Graphs could not be formatted to SPARQL query results XML format",
)
.into()),
}
}
fn write_escaped_json_string(s: &str, mut sink: impl Write) -> Result<(), EvaluationError> {
fn write_escaped_json_string(s: &str, sink: &mut impl Write) -> Result<(), EvaluationError> {
sink.write_all(b"\"")?;
for c in s.chars() {
match c {

@ -129,7 +129,7 @@ impl<'a> SimpleUpdateEvaluator<'a> {
.into_iter()
.map(|t| {
Ok(if let Some(t) = t {
let r: Result<_, EvaluationError> = t.on_each_id(|id| {
let r: Result<_, EvaluationError> = t.on_each_id(&mut |id| {
self.storage.insert_str(
id,
&dataset.get_str(id)?.ok_or_else(|| {

@ -88,8 +88,30 @@ fn write_solutions(solutions: QuerySolutionIter, sink: impl Write) -> Result<(),
writer
.write_event(Event::Start(binding_tag))
.map_err(map_xml_error)?;
match value {
Term::NamedNode(uri) => {
write_xml_term(value.as_ref(), &mut writer)?;
writer
.write_event(Event::End(BytesEnd::borrowed(b"binding")))
.map_err(map_xml_error)?;
}
writer
.write_event(Event::End(BytesEnd::borrowed(b"result")))
.map_err(map_xml_error)?;
}
writer
.write_event(Event::End(BytesEnd::borrowed(b"results")))
.map_err(map_xml_error)?;
writer
.write_event(Event::End(BytesEnd::borrowed(b"sparql")))
.map_err(map_xml_error)?;
Ok(())
}
fn write_xml_term(
term: TermRef<'_>,
writer: &mut Writer<impl Write>,
) -> Result<(), EvaluationError> {
match term {
TermRef::NamedNode(uri) => {
writer
.write_event(Event::Start(BytesStart::borrowed_name(b"uri")))
.map_err(map_xml_error)?;
@ -100,7 +122,7 @@ fn write_solutions(solutions: QuerySolutionIter, sink: impl Write) -> Result<(),
.write_event(Event::End(BytesEnd::borrowed(b"uri")))
.map_err(map_xml_error)?;
}
Term::BlankNode(bnode) => {
TermRef::BlankNode(bnode) => {
writer
.write_event(Event::Start(BytesStart::borrowed_name(b"bnode")))
.map_err(map_xml_error)?;
@ -111,7 +133,7 @@ fn write_solutions(solutions: QuerySolutionIter, sink: impl Write) -> Result<(),
.write_event(Event::End(BytesEnd::borrowed(b"bnode")))
.map_err(map_xml_error)?;
}
Term::Literal(literal) => {
TermRef::Literal(literal) => {
let mut literal_tag = BytesStart::borrowed_name(b"literal");
if let Some(language) = literal.language() {
literal_tag.push_attribute(("xml:lang", language));
@ -128,21 +150,36 @@ fn write_solutions(solutions: QuerySolutionIter, sink: impl Write) -> Result<(),
.write_event(Event::End(BytesEnd::borrowed(b"literal")))
.map_err(map_xml_error)?;
}
}
TermRef::Triple(triple) => {
writer
.write_event(Event::End(BytesEnd::borrowed(b"binding")))
.write_event(Event::Start(BytesStart::borrowed_name(b"triple")))
.map_err(map_xml_error)?;
}
writer
.write_event(Event::End(BytesEnd::borrowed(b"result")))
.write_event(Event::Start(BytesStart::borrowed_name(b"subject")))
.map_err(map_xml_error)?;
}
write_xml_term(triple.subject.as_ref().into(), writer)?;
writer
.write_event(Event::End(BytesEnd::borrowed(b"results")))
.write_event(Event::End(BytesEnd::borrowed(b"subject")))
.map_err(map_xml_error)?;
writer
.write_event(Event::End(BytesEnd::borrowed(b"sparql")))
.write_event(Event::Start(BytesStart::borrowed_name(b"predicate")))
.map_err(map_xml_error)?;
write_xml_term(triple.predicate.as_ref().into(), writer)?;
writer
.write_event(Event::End(BytesEnd::borrowed(b"predicate")))
.map_err(map_xml_error)?;
writer
.write_event(Event::Start(BytesStart::borrowed_name(b"object")))
.map_err(map_xml_error)?;
write_xml_term(triple.object.as_ref(), writer)?;
writer
.write_event(Event::End(BytesEnd::borrowed(b"object")))
.map_err(map_xml_error)?;
writer
.write_event(Event::End(BytesEnd::borrowed(b"triple")))
.map_err(map_xml_error)?;
}
}
Ok(())
}

@ -1,10 +1,11 @@
use crate::error::invalid_data_error;
use crate::model::xsd::*;
use crate::storage::numeric_encoder::{EncodedQuad, EncodedTerm, StrHash};
use crate::storage::numeric_encoder::{EncodedQuad, EncodedTerm, EncodedTriple, StrHash};
use crate::storage::small_string::SmallString;
use std::io;
use std::io::{Cursor, Read};
use std::mem::size_of;
use std::rc::Rc;
pub const LATEST_STORAGE_VERSION: u64 = 1;
pub const WRITTEN_TERM_MAX_SIZE: usize = size_of::<u8>() + 2 * size_of::<StrHash>();
@ -13,7 +14,8 @@ pub const WRITTEN_TERM_MAX_SIZE: usize = size_of::<u8>() + 2 * size_of::<StrHash
// 1-7: usual named nodes (except prefixes c.f. later)
// 8-15: blank nodes
// 16-47: literals
// 48-64: future use
// 48-55: triples
// 56-64: future use
// 64-127: default named node prefixes
// 128-255: custom named node prefixes
const TYPE_NAMED_NODE_ID: u8 = 1;
@ -45,6 +47,7 @@ const TYPE_G_MONTH_LITERAL: u8 = 41;
const TYPE_DURATION_LITERAL: u8 = 42;
const TYPE_YEAR_MONTH_DURATION_LITERAL: u8 = 43;
const TYPE_DAY_TIME_DURATION_LITERAL: u8 = 44;
const TYPE_TRIPLE: u8 = 48;
#[derive(Clone, Copy)]
pub enum QuadEncoding {
@ -396,6 +399,11 @@ impl<R: Read> TermReader for R {
DayTimeDuration::from_be_bytes(buffer),
))
}
TYPE_TRIPLE => Ok(EncodedTerm::Triple(Rc::new(EncodedTriple {
subject: self.read_term()?,
predicate: self.read_term()?,
object: self.read_term()?,
}))),
_ => Err(invalid_data_error("the term buffer has an invalid type id")),
}
}
@ -621,6 +629,12 @@ pub fn write_term(sink: &mut Vec<u8>, term: &EncodedTerm) {
sink.push(TYPE_DAY_TIME_DURATION_LITERAL);
sink.extend_from_slice(&value.to_be_bytes())
}
EncodedTerm::Triple(value) => {
sink.push(TYPE_TRIPLE);
write_term(sink, &value.subject);
write_term(sink, &value.predicate);
write_term(sink, &value.object);
}
}
}
@ -715,6 +729,12 @@ mod tests {
NamedNode::new_unchecked("http://foo.com"),
)
.into(),
Triple::new(
NamedNode::new_unchecked("http://foo.com"),
NamedNode::new_unchecked("http://bar.com"),
Literal::from(true),
)
.into(),
];
for term in terms {
let encoded = store.encode_term(term.as_ref()).unwrap();

@ -14,6 +14,7 @@ use std::error::Error;
use std::fmt::Debug;
use std::hash::Hash;
use std::hash::Hasher;
use std::rc::Rc;
use std::{fmt, io, str};
#[derive(Eq, PartialEq, Debug, Clone, Copy, Hash)]
@ -101,6 +102,7 @@ pub enum EncodedTerm {
DurationLiteral(Duration),
YearMonthDurationLiteral(YearMonthDuration),
DayTimeDurationLiteral(DayTimeDuration),
Triple(Rc<EncodedTriple>),
}
impl PartialEq for EncodedTerm {
@ -214,6 +216,7 @@ impl PartialEq for EncodedTerm {
(Self::DurationLiteral(a), Self::DurationLiteral(b)) => a == b,
(Self::YearMonthDurationLiteral(a), Self::YearMonthDurationLiteral(b)) => a == b,
(Self::DayTimeDurationLiteral(a), Self::DayTimeDurationLiteral(b)) => a == b,
(Self::Triple(a), Self::Triple(b)) => a == b,
(_, _) => false,
}
}
@ -277,6 +280,7 @@ impl Hash for EncodedTerm {
Self::DurationLiteral(value) => value.hash(state),
Self::YearMonthDurationLiteral(value) => value.hash(state),
Self::DayTimeDurationLiteral(value) => value.hash(state),
Self::Triple(value) => value.hash(state),
}
}
}
@ -338,7 +342,7 @@ impl EncodedTerm {
pub fn on_each_id<E>(
&self,
mut callback: impl FnMut(&StrHash) -> Result<(), E>,
callback: &mut impl FnMut(&StrHash) -> Result<(), E>,
) -> Result<(), E> {
match self {
Self::NamedNode { iri_id } => {
@ -373,6 +377,11 @@ impl EncodedTerm {
callback(value_id)?;
callback(datatype_id)?;
}
Self::Triple(triple) => {
triple.subject.on_each_id(callback)?;
triple.predicate.on_each_id(callback)?;
triple.object.on_each_id(callback)?;
}
_ => (),
}
Ok(())
@ -462,6 +471,19 @@ impl From<DayTimeDuration> for EncodedTerm {
}
}
impl From<EncodedTriple> for EncodedTerm {
fn from(value: EncodedTriple) -> Self {
Self::Triple(Rc::new(value))
}
}
#[derive(Eq, PartialEq, Debug, Clone, Hash)]
pub struct EncodedTriple {
pub subject: EncodedTerm,
pub predicate: EncodedTerm,
pub object: EncodedTerm,
}
#[derive(Eq, PartialEq, Debug, Clone, Hash)]
pub struct EncodedQuad {
pub subject: EncodedTerm,
@ -613,6 +635,9 @@ pub(crate) fn get_encoded_subject(term: SubjectRef<'_>) -> EncodedTerm {
match term {
SubjectRef::NamedNode(named_node) => get_encoded_named_node(named_node),
SubjectRef::BlankNode(blank_node) => get_encoded_blank_node(blank_node),
SubjectRef::Triple(triple) => {
EncodedTerm::Triple(Rc::new(get_encoded_triple(triple.as_ref())))
}
}
}
@ -621,6 +646,9 @@ pub(crate) fn get_encoded_term(term: TermRef<'_>) -> EncodedTerm {
TermRef::NamedNode(named_node) => get_encoded_named_node(named_node),
TermRef::BlankNode(blank_node) => get_encoded_blank_node(blank_node),
TermRef::Literal(literal) => get_encoded_literal(literal),
TermRef::Triple(triple) => {
EncodedTerm::Triple(Rc::new(get_encoded_triple(triple.as_ref())))
}
}
}
@ -632,6 +660,14 @@ pub(crate) fn get_encoded_graph_name(name: GraphNameRef<'_>) -> EncodedTerm {
}
}
pub(crate) fn get_encoded_triple(quad: TripleRef<'_>) -> EncodedTriple {
EncodedTriple {
subject: get_encoded_subject(quad.subject),
predicate: get_encoded_named_node(quad.predicate),
object: get_encoded_term(quad.object),
}
}
pub(crate) fn get_encoded_quad(quad: QuadRef<'_>) -> EncodedQuad {
EncodedQuad {
subject: get_encoded_subject(quad.subject),
@ -670,6 +706,9 @@ pub(crate) trait WriteEncoder: StrContainer {
match term {
SubjectRef::NamedNode(named_node) => self.encode_named_node(named_node),
SubjectRef::BlankNode(blank_node) => self.encode_blank_node(blank_node),
SubjectRef::Triple(triple) => Ok(EncodedTerm::Triple(Rc::new(
self.encode_triple(triple.as_ref())?,
))),
}
}
@ -678,6 +717,9 @@ pub(crate) trait WriteEncoder: StrContainer {
TermRef::NamedNode(named_node) => self.encode_named_node(named_node),
TermRef::BlankNode(blank_node) => self.encode_blank_node(blank_node),
TermRef::Literal(literal) => self.encode_literal(literal),
TermRef::Triple(triple) => Ok(EncodedTerm::Triple(Rc::new(
self.encode_triple(triple.as_ref())?,
))),
}
}
@ -689,6 +731,14 @@ pub(crate) trait WriteEncoder: StrContainer {
}
}
fn encode_triple(&self, quad: TripleRef<'_>) -> Result<EncodedTriple, Self::Error> {
Ok(EncodedTriple {
subject: self.encode_subject(quad.subject)?,
predicate: self.encode_named_node(quad.predicate)?,
object: self.encode_term(quad.object)?,
})
}
fn encode_quad(&self, quad: QuadRef<'_>) -> Result<EncodedQuad, Self::Error> {
Ok(EncodedQuad {
subject: self.encode_subject(quad.subject)?,
@ -980,7 +1030,10 @@ pub(crate) trait Decoder: StrLookup {
Term::NamedNode(named_node) => Ok(named_node.into()),
Term::BlankNode(blank_node) => Ok(blank_node.into()),
Term::Literal(_) => Err(DecoderError::Decoder {
msg: "A literal has ben found instead of a named node".to_owned(),
msg: "A literal has been found instead of a named node".to_owned(),
}),
Term::Triple(_) => Err(DecoderError::Decoder {
msg: "A triple has been found instead of a named node".to_owned(),
}),
}
}
@ -995,19 +1048,44 @@ pub(crate) trait Decoder: StrLookup {
msg: "A blank node has been found instead of a named node".to_owned(),
}),
Term::Literal(_) => Err(DecoderError::Decoder {
msg: "A literal has ben found instead of a named node".to_owned(),
msg: "A literal has been found instead of a named node".to_owned(),
}),
Term::Triple(_) => Err(DecoderError::Decoder {
msg: "A triple has been found instead of a named node".to_owned(),
}),
}
}
fn decode_triple(&self, encoded: &EncodedTriple) -> Result<Triple, DecoderError<Self::Error>> {
Ok(Triple::new(
self.decode_subject(&encoded.subject)?,
self.decode_named_node(&encoded.predicate)?,
self.decode_term(&encoded.object)?,
))
}
fn decode_quad(&self, encoded: &EncodedQuad) -> Result<Quad, DecoderError<Self::Error>> {
Ok(Quad::new(
self.decode_subject(&encoded.subject)?,
self.decode_named_node(&encoded.predicate)?,
self.decode_term(&encoded.object)?,
match &encoded.graph_name {
EncodedTerm::DefaultGraph => None,
graph_name => Some(self.decode_subject(graph_name)?),
if encoded.graph_name == EncodedTerm::DefaultGraph {
GraphName::DefaultGraph
} else {
match self.decode_term(&encoded.graph_name)? {
Term::NamedNode(named_node) => named_node.into(),
Term::BlankNode(blank_node) => blank_node.into(),
Term::Literal(_) => {
return Err(DecoderError::Decoder {
msg: "A literal is not a valid graph name".to_owned(),
})
}
Term::Triple(_) => {
return Err(DecoderError::Decoder {
msg: "A triple is not a valid graph name".to_owned(),
})
}
}
},
))
}
@ -1089,6 +1167,7 @@ impl<S: StrLookup> Decoder for S {
EncodedTerm::DurationLiteral(value) => Ok(Literal::from(*value).into()),
EncodedTerm::YearMonthDurationLiteral(value) => Ok(Literal::from(*value).into()),
EncodedTerm::DayTimeDurationLiteral(value) => Ok(Literal::from(*value).into()),
EncodedTerm::Triple(triple) => Ok(self.decode_triple(triple)?.into()),
}
}
}

@ -5,14 +5,12 @@
//! use oxigraph::store::Store;
//! use oxigraph::sparql::QueryResults;
//! use oxigraph::model::*;
//! # use std::fs::remove_dir_all;
//!
//! # {
//! let store = Store::open("example.db")?;
//! let store = Store::new()?;
//!
//! // insertion
//! let ex = NamedNode::new("http://example.com")?;
//! let quad = Quad::new(ex.clone(), ex.clone(), ex.clone(), None);
//! let quad = Quad::new(ex.clone(), ex.clone(), ex.clone(), GraphName::DefaultGraph);
//! store.insert(&quad)?;
//!
//! // quad filter
@ -23,9 +21,6 @@
//! if let QueryResults::Solutions(mut solutions) = store.query("SELECT ?s WHERE { ?s ?p ?o }")? {
//! assert_eq!(solutions.next().unwrap()?.get("s"), Some(&ex.into()));
//! };
//! #
//! # };
//! # remove_dir_all("example.db")?;
//! # Result::<_,Box<dyn std::error::Error>>::Ok(())
//! ```
@ -69,7 +64,7 @@ use std::{fmt, io, str};
///
/// // insertion
/// let ex = NamedNode::new("http://example.com")?;
/// let quad = Quad::new(ex.clone(), ex.clone(), ex.clone(), None);
/// let quad = Quad::new(ex.clone(), ex.clone(), ex.clone(), GraphName::DefaultGraph);
/// store.insert(&quad)?;
///
/// // quad filter
@ -119,7 +114,7 @@ impl Store {
///
/// // insertions
/// let ex = NamedNodeRef::new("http://example.com")?;
/// store.insert(QuadRef::new(ex, ex, ex, None))?;
/// store.insert(QuadRef::new(ex, ex, ex, GraphNameRef::DefaultGraph))?;
///
/// // SPARQL query
/// if let QueryResults::Solutions(mut solutions) = store.query("SELECT ?s WHERE { ?s ?p ?o }")? {
@ -154,7 +149,7 @@ impl Store {
///
/// // insertion
/// let ex = NamedNode::new("http://example.com")?;
/// let quad = Quad::new(ex.clone(), ex.clone(), ex.clone(), None);
/// let quad = Quad::new(ex.clone(), ex.clone(), ex.clone(), GraphName::DefaultGraph);
/// store.insert(&quad)?;
///
/// // quad filter by object
@ -220,7 +215,7 @@ impl Store {
///
/// // we inspect the store contents
/// let ex = NamedNodeRef::new("http://example.com").unwrap();
/// assert!(store.contains(QuadRef::new(ex, ex, ex, None))?);
/// assert!(store.contains(QuadRef::new(ex, ex, ex, GraphNameRef::DefaultGraph))?);
/// # Result::<_,Box<dyn std::error::Error>>::Ok(())
/// ```
pub fn update(
@ -298,7 +293,7 @@ impl Store {
///
/// // we inspect the store contents
/// let ex = NamedNodeRef::new("http://example.com")?;
/// assert!(store.contains(QuadRef::new(ex, ex, ex, None))?);
/// assert!(store.contains(QuadRef::new(ex, ex, ex, GraphNameRef::DefaultGraph))?);
/// # Result::<_,Box<dyn std::error::Error>>::Ok(())
/// ```
///
@ -441,12 +436,12 @@ impl Store {
/// Usage example:
/// ```
/// use oxigraph::store::Store;
/// use oxigraph::model::{NamedNode, QuadRef, Subject};
/// use oxigraph::model::*;
///
/// let ex = NamedNode::new("http://example.com")?;
/// let store = Store::new()?;
/// store.insert(QuadRef::new(&ex, &ex, &ex, &ex))?;
/// store.insert(QuadRef::new(&ex, &ex, &ex, None))?;
/// store.insert(QuadRef::new(&ex, &ex, &ex, GraphNameRef::DefaultGraph))?;
/// assert_eq!(vec![Subject::from(ex)], store.named_graphs().collect::<Result<Vec<_>,_>>()?);
/// # Result::<_,Box<dyn std::error::Error>>::Ok(())
/// ```
@ -560,12 +555,12 @@ impl Store {
/// Usage example:
/// ```
/// use oxigraph::store::Store;
/// use oxigraph::model::{NamedNodeRef, QuadRef};
/// use oxigraph::model::*;
///
/// let ex = NamedNodeRef::new("http://example.com")?;
/// let store = Store::new()?;
/// store.insert(QuadRef::new(ex, ex, ex, ex))?;
/// store.insert(QuadRef::new(ex, ex, ex, None))?;
/// store.insert(QuadRef::new(ex, ex, ex, GraphNameRef::DefaultGraph))?;
/// assert_eq!(2, store.len());
///
/// store.clear()?;
@ -616,7 +611,7 @@ impl Transaction<'_> {
///
/// // we inspect the store content
/// let ex = NamedNodeRef::new("http://example.com")?;
/// assert!(store.contains(QuadRef::new(ex, ex, ex, None))?);
/// assert!(store.contains(QuadRef::new(ex, ex, ex, GraphNameRef::DefaultGraph))?);
/// # Result::<_, Box<dyn std::error::Error>>::Ok(())
/// ```
///
@ -769,7 +764,12 @@ fn store() -> Result<(), io::Error> {
let main_o = Term::from(Literal::from(1));
let main_g = GraphName::from(BlankNode::default());
let default_quad = Quad::new(main_s.clone(), main_p.clone(), main_o.clone(), None);
let default_quad = Quad::new(
main_s.clone(),
main_p.clone(),
main_o.clone(),
GraphName::DefaultGraph,
);
let named_quad = Quad::new(
main_s.clone(),
main_p.clone(),
@ -777,23 +777,33 @@ fn store() -> Result<(), io::Error> {
main_g.clone(),
);
let default_quads = vec![
Quad::new(main_s.clone(), main_p.clone(), Literal::from(0), None),
Quad::new(
main_s.clone(),
main_p.clone(),
Literal::from(0),
GraphName::DefaultGraph,
),
default_quad.clone(),
Quad::new(
main_s.clone(),
main_p.clone(),
Literal::from(200000000),
None,
GraphName::DefaultGraph,
),
];
let all_quads = vec![
Quad::new(main_s.clone(), main_p.clone(), Literal::from(0), None),
Quad::new(
main_s.clone(),
main_p.clone(),
Literal::from(0),
GraphName::DefaultGraph,
),
default_quad.clone(),
Quad::new(
main_s.clone(),
main_p.clone(),
Literal::from(200000000),
None,
GraphName::DefaultGraph,
),
named_quad.clone(),
];

@ -78,7 +78,12 @@ fn quads(graph_name: impl Into<GraphNameRef<'static>>) -> Vec<QuadRef<'static>>
#[test]
fn test_load_graph() -> io::Result<()> {
let store = Store::new()?;
store.load_graph(Cursor::new(DATA), GraphFormat::Turtle, None, None)?;
store.load_graph(
Cursor::new(DATA),
GraphFormat::Turtle,
GraphNameRef::DefaultGraph,
None,
)?;
for q in quads(GraphNameRef::DefaultGraph) {
assert!(store.contains(q)?);
}
@ -103,7 +108,11 @@ fn test_dump_graph() -> io::Result<()> {
}
let mut buffer = Vec::new();
store.dump_graph(&mut buffer, GraphFormat::NTriples, None)?;
store.dump_graph(
&mut buffer,
GraphFormat::NTriples,
GraphNameRef::DefaultGraph,
)?;
assert_eq!(
buffer.into_iter().filter(|c| *c == b'\n').count(),
NUMBER_OF_TRIPLES
@ -131,7 +140,12 @@ fn test_dump_dataset() -> io::Result<()> {
fn test_transaction_load_graph() -> io::Result<()> {
let store = Store::new()?;
store.transaction(|t| {
t.load_graph(Cursor::new(DATA), GraphFormat::Turtle, None, None)?;
t.load_graph(
Cursor::new(DATA),
GraphFormat::Turtle,
GraphNameRef::DefaultGraph,
None,
)?;
Ok(()) as Result<_, ConflictableTransactionError<io::Error>>
})?;
for q in quads(GraphNameRef::DefaultGraph) {

@ -408,6 +408,7 @@ impl PyObjectProtocol for PyDefaultGraph {
pub enum PySubject {
NamedNode(PyNamedNode),
BlankNode(PyBlankNode),
Triple(PyTriple),
}
impl From<PySubject> for Subject {
@ -415,6 +416,7 @@ impl From<PySubject> for Subject {
match node {
PySubject::NamedNode(node) => node.into(),
PySubject::BlankNode(node) => node.into(),
PySubject::Triple(triple) => triple.into(),
}
}
}
@ -424,6 +426,7 @@ impl From<Subject> for PySubject {
match node {
Subject::NamedNode(node) => PySubject::NamedNode(node.into()),
Subject::BlankNode(node) => PySubject::BlankNode(node.into()),
Subject::Triple(triple) => PySubject::Triple(triple.as_ref().clone().into()),
}
}
}
@ -433,6 +436,7 @@ impl IntoPy<PyObject> for PySubject {
match self {
PySubject::NamedNode(node) => node.into_py(py),
PySubject::BlankNode(node) => node.into_py(py),
PySubject::Triple(triple) => triple.into_py(py),
}
}
}
@ -442,6 +446,7 @@ pub enum PyTerm {
NamedNode(PyNamedNode),
BlankNode(PyBlankNode),
Literal(PyLiteral),
Triple(PyTriple),
}
impl From<PyTerm> for Term {
@ -450,6 +455,7 @@ impl From<PyTerm> for Term {
PyTerm::NamedNode(node) => node.into(),
PyTerm::BlankNode(node) => node.into(),
PyTerm::Literal(literal) => literal.into(),
PyTerm::Triple(triple) => triple.into(),
}
}
}
@ -460,6 +466,7 @@ impl From<Term> for PyTerm {
Term::NamedNode(node) => PyTerm::NamedNode(node.into()),
Term::BlankNode(node) => PyTerm::BlankNode(node.into()),
Term::Literal(literal) => PyTerm::Literal(literal.into()),
Term::Triple(triple) => PyTerm::Triple(triple.as_ref().clone().into()),
}
}
}
@ -470,6 +477,7 @@ impl IntoPy<PyObject> for PyTerm {
PyTerm::NamedNode(node) => node.into_py(py),
PyTerm::BlankNode(node) => node.into_py(py),
PyTerm::Literal(literal) => literal.into_py(py),
PyTerm::Triple(triple) => triple.into_py(py),
}
}
}
@ -505,14 +513,26 @@ impl From<Triple> for PyTriple {
}
impl From<PyTriple> for Triple {
fn from(node: PyTriple) -> Self {
node.inner
fn from(triple: PyTriple) -> Self {
triple.inner
}
}
impl<'a> From<&'a PyTriple> for TripleRef<'a> {
fn from(node: &'a PyTriple) -> Self {
node.inner.as_ref()
fn from(triple: &'a PyTriple) -> Self {
triple.inner.as_ref()
}
}
impl From<PyTriple> for Subject {
fn from(triple: PyTriple) -> Self {
triple.inner.into()
}
}
impl From<PyTriple> for Term {
fn from(triple: PyTriple) -> Self {
triple.inner.into()
}
}
@ -562,13 +582,7 @@ impl PyObjectProtocol for PyTriple {
fn __repr__(&self) -> String {
let mut buffer = String::new();
buffer.push_str("<Triple subject=");
term_repr(self.inner.subject.as_ref().into(), &mut buffer);
buffer.push_str(" predicate=");
named_node_repr(self.inner.predicate.as_ref(), &mut buffer);
buffer.push_str(" object=");
term_repr(self.inner.object.as_ref(), &mut buffer);
buffer.push('>');
triple_repr(self.inner.as_ref(), &mut buffer);
buffer
}
@ -1109,6 +1123,7 @@ pub fn term_repr(term: TermRef<'_>, buffer: &mut String) {
TermRef::NamedNode(node) => named_node_repr(node, buffer),
TermRef::BlankNode(node) => blank_node_repr(node, buffer),
TermRef::Literal(literal) => literal_repr(literal, buffer),
TermRef::Triple(triple) => triple_repr(triple.as_ref(), buffer),
}
}
@ -1120,6 +1135,16 @@ fn graph_name_repr(term: GraphNameRef<'_>, buffer: &mut String) {
}
}
fn triple_repr(triple: TripleRef<'_>, buffer: &mut String) {
buffer.push_str("<Triple subject=");
term_repr(triple.subject.into(), buffer);
buffer.push_str(" predicate=");
named_node_repr(triple.predicate, buffer);
buffer.push_str(" object=");
term_repr(triple.object, buffer);
buffer.push('>');
}
#[pyclass(module = "oxigraph")]
pub struct TripleComponentsIter {
inner: IntoIter<Term>,

@ -76,6 +76,31 @@ class TestTriple(unittest.TestCase):
self.assertEqual(t.predicate, NamedNode("http://example.com/p"))
self.assertEqual(t.object, NamedNode("http://example.com/o"))
def test_rdf_star_constructor(self):
t = Triple(
Triple(
NamedNode("http://example.com/ss"),
NamedNode("http://example.com/sp"),
NamedNode("http://example.com/so")
),
NamedNode("http://example.com/p"),
Triple(
NamedNode("http://example.com/os"),
NamedNode("http://example.com/op"),
NamedNode("http://example.com/oo")
), )
self.assertEqual(t.subject, Triple(
NamedNode("http://example.com/ss"),
NamedNode("http://example.com/sp"),
NamedNode("http://example.com/so")
))
self.assertEqual(t.predicate, NamedNode("http://example.com/p"))
self.assertEqual(t.object, Triple(
NamedNode("http://example.com/os"),
NamedNode("http://example.com/op"),
NamedNode("http://example.com/oo")
))
def test_mapping(self):
t = Triple(
NamedNode("http://example.com/s"),

@ -6,6 +6,7 @@ from pyoxigraph import *
foo = NamedNode("http://foo")
bar = NamedNode("http://bar")
baz = NamedNode("http://baz")
triple = Triple(foo, foo, foo)
graph = NamedNode("http://graph")
@ -15,7 +16,9 @@ class TestStore(unittest.TestCase):
store.add(Quad(foo, bar, baz))
store.add(Quad(foo, bar, baz, DefaultGraph()))
store.add(Quad(foo, bar, baz, graph))
self.assertEqual(len(store), 2)
store.add(Quad(triple, bar, baz))
store.add(Quad(foo, bar, triple))
self.assertEqual(len(store), 4)
def test_remove(self):
store = Store()

Loading…
Cancel
Save