Basic RDF-star support

No parsing and SPARQL support yet
pull/171/head
Tpt 4 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 // insertion
let ex = NamedNode::new("http://example.com")?; 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)?; store.insert(&quad)?;
// quad filter // quad filter

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

@ -21,7 +21,7 @@
//! //!
//! // insertion //! // insertion
//! let ex = NamedNode::new("http://example.com")?; //! 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)?; //! store.insert(&quad)?;
//! //!
//! // quad filter //! // quad filter

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

@ -2,28 +2,35 @@
use crate::model::*; use crate::model::*;
use lasso::{Key, Rodeo, Spur}; use lasso::{Key, Rodeo, Spur};
use std::collections::HashMap;
use std::convert::TryInto; 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)] #[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Copy, Hash)]
pub struct InternedNamedNode { pub struct InternedNamedNode {
id: Spur, id: Spur,
} }
impl InternedNamedNode { 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 { 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 { 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> { pub fn decode_from<'a>(&self, interner: &'a Interner) -> NamedNodeRef<'a> {
NamedNodeRef::new_unchecked(interner.resolve(&self.id)) NamedNodeRef::new_unchecked(interner.strings.resolve(&self.id))
} }
pub fn first() -> Self { pub fn first() -> Self {
@ -49,20 +56,20 @@ pub struct InternedBlankNode {
} }
impl 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 { 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 { 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> { pub fn decode_from<'a>(&self, interner: &'a Interner) -> BlankNodeRef<'a> {
BlankNodeRef::new_unchecked(interner.resolve(&self.id)) BlankNodeRef::new_unchecked(interner.strings.resolve(&self.id))
} }
pub fn next(&self) -> Self { pub fn next(&self) -> Self {
@ -88,13 +95,13 @@ pub enum InternedLiteral {
} }
impl InternedLiteral { impl InternedLiteral {
pub fn encoded_into(literal: LiteralRef<'_>, interner: &mut Rodeo) -> Self { pub fn encoded_into(literal: LiteralRef<'_>, interner: &mut Interner) -> Self {
let value_id = interner.get_or_intern(literal.value()); let value_id = interner.strings.get_or_intern(literal.value());
if literal.is_plain() { if literal.is_plain() {
if let Some(language) = literal.language() { if let Some(language) = literal.language() {
Self::LanguageTaggedString { Self::LanguageTaggedString {
value_id, value_id,
language_id: interner.get_or_intern(language), language_id: interner.strings.get_or_intern(language),
} }
} else { } else {
Self::String { value_id } Self::String { value_id }
@ -107,13 +114,13 @@ impl InternedLiteral {
} }
} }
pub fn encoded_from(literal: LiteralRef<'_>, interner: &Rodeo) -> Option<Self> { pub fn encoded_from(literal: LiteralRef<'_>, interner: &Interner) -> Option<Self> {
let value_id = interner.get(literal.value())?; let value_id = interner.strings.get(literal.value())?;
Some(if literal.is_plain() { Some(if literal.is_plain() {
if let Some(language) = literal.language() { if let Some(language) = literal.language() {
Self::LanguageTaggedString { Self::LanguageTaggedString {
value_id, value_id,
language_id: interner.get(language)?, language_id: interner.strings.get(language)?,
} }
} else { } else {
Self::String { value_id } 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 { match self {
InternedLiteral::String { value_id } => { InternedLiteral::String { value_id } => {
LiteralRef::new_simple_literal(interner.resolve(value_id)) LiteralRef::new_simple_literal(interner.strings.resolve(value_id))
} }
InternedLiteral::LanguageTaggedString { InternedLiteral::LanguageTaggedString {
value_id, value_id,
language_id, language_id,
} => LiteralRef::new_language_tagged_literal_unchecked( } => LiteralRef::new_language_tagged_literal_unchecked(
interner.resolve(value_id), interner.strings.resolve(value_id),
interner.resolve(language_id), interner.strings.resolve(language_id),
), ),
InternedLiteral::TypedLiteral { value_id, datatype } => LiteralRef::new_typed_literal( InternedLiteral::TypedLiteral { value_id, datatype } => LiteralRef::new_typed_literal(
interner.resolve(value_id), interner.strings.resolve(value_id),
datatype.decode_from(interner), datatype.decode_from(interner),
), ),
} }
@ -169,10 +176,11 @@ impl InternedLiteral {
pub enum InternedSubject { pub enum InternedSubject {
NamedNode(InternedNamedNode), NamedNode(InternedNamedNode),
BlankNode(InternedBlankNode), BlankNode(InternedBlankNode),
Triple(Box<InternedTriple>),
} }
impl InternedSubject { impl InternedSubject {
pub fn encoded_into(node: SubjectRef<'_>, interner: &mut Rodeo) -> Self { pub fn encoded_into(node: SubjectRef<'_>, interner: &mut Interner) -> Self {
match node { match node {
SubjectRef::NamedNode(node) => { SubjectRef::NamedNode(node) => {
Self::NamedNode(InternedNamedNode::encoded_into(node, interner)) Self::NamedNode(InternedNamedNode::encoded_into(node, interner))
@ -180,10 +188,14 @@ impl InternedSubject {
SubjectRef::BlankNode(node) => { SubjectRef::BlankNode(node) => {
Self::BlankNode(InternedBlankNode::encoded_into(node, interner)) 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 { Some(match node {
SubjectRef::NamedNode(node) => { SubjectRef::NamedNode(node) => {
Self::NamedNode(InternedNamedNode::encoded_from(node, interner)?) Self::NamedNode(InternedNamedNode::encoded_from(node, interner)?)
@ -191,13 +203,18 @@ impl InternedSubject {
SubjectRef::BlankNode(node) => { SubjectRef::BlankNode(node) => {
Self::BlankNode(InternedBlankNode::encoded_from(node, interner)?) 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 { match self {
Self::NamedNode(node) => SubjectRef::NamedNode(node.decode_from(interner)), Self::NamedNode(node) => SubjectRef::NamedNode(node.decode_from(interner)),
Self::BlankNode(node) => SubjectRef::BlankNode(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 { match self {
Self::NamedNode(node) => Self::NamedNode(node.next()), Self::NamedNode(node) => Self::NamedNode(node.next()),
Self::BlankNode(node) => Self::BlankNode(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 { impl InternedGraphName {
pub fn encoded_into(node: GraphNameRef<'_>, interner: &mut Rodeo) -> Self { pub fn encoded_into(node: GraphNameRef<'_>, interner: &mut Interner) -> Self {
match node { match node {
GraphNameRef::DefaultGraph => Self::DefaultGraph, GraphNameRef::DefaultGraph => Self::DefaultGraph,
GraphNameRef::NamedNode(node) => { 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 { Some(match node {
GraphNameRef::DefaultGraph => Self::DefaultGraph, GraphNameRef::DefaultGraph => Self::DefaultGraph,
GraphNameRef::NamedNode(node) => { 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 { match self {
Self::DefaultGraph => GraphNameRef::DefaultGraph, Self::DefaultGraph => GraphNameRef::DefaultGraph,
Self::NamedNode(node) => GraphNameRef::NamedNode(node.decode_from(interner)), Self::NamedNode(node) => GraphNameRef::NamedNode(node.decode_from(interner)),
@ -279,10 +297,11 @@ pub enum InternedTerm {
NamedNode(InternedNamedNode), NamedNode(InternedNamedNode),
BlankNode(InternedBlankNode), BlankNode(InternedBlankNode),
Literal(InternedLiteral), Literal(InternedLiteral),
Triple(Box<InternedTriple>),
} }
impl InternedTerm { impl InternedTerm {
pub fn encoded_into(term: TermRef<'_>, interner: &mut Rodeo) -> Self { pub fn encoded_into(term: TermRef<'_>, interner: &mut Interner) -> Self {
match term { match term {
TermRef::NamedNode(term) => { TermRef::NamedNode(term) => {
Self::NamedNode(InternedNamedNode::encoded_into(term, interner)) Self::NamedNode(InternedNamedNode::encoded_into(term, interner))
@ -291,10 +310,14 @@ impl InternedTerm {
Self::BlankNode(InternedBlankNode::encoded_into(term, interner)) Self::BlankNode(InternedBlankNode::encoded_into(term, interner))
} }
TermRef::Literal(term) => Self::Literal(InternedLiteral::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 { Some(match term {
TermRef::NamedNode(term) => { TermRef::NamedNode(term) => {
Self::NamedNode(InternedNamedNode::encoded_from(term, interner)?) Self::NamedNode(InternedNamedNode::encoded_from(term, interner)?)
@ -303,14 +326,19 @@ impl InternedTerm {
Self::BlankNode(InternedBlankNode::encoded_from(term, interner)?) Self::BlankNode(InternedBlankNode::encoded_from(term, interner)?)
} }
TermRef::Literal(term) => Self::Literal(InternedLiteral::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 { match self {
Self::NamedNode(term) => TermRef::NamedNode(term.decode_from(interner)), Self::NamedNode(term) => TermRef::NamedNode(term.decode_from(interner)),
Self::BlankNode(term) => TermRef::BlankNode(term.decode_from(interner)), Self::BlankNode(term) => TermRef::BlankNode(term.decode_from(interner)),
Self::Literal(term) => TermRef::Literal(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::NamedNode(node) => Self::NamedNode(node.next()),
Self::BlankNode(node) => Self::BlankNode(node.next()), Self::BlankNode(node) => Self::BlankNode(node.next()),
Self::Literal(node) => Self::Literal(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 { fn fist_spur() -> Spur {
Spur::try_from_usize(0).unwrap() Spur::try_from_usize(0).unwrap()
} }

@ -191,26 +191,26 @@ impl<'a> From<GraphNameRef<'a>> for Option<TermRef<'a>> {
impl TTerm for Subject { impl TTerm for Subject {
fn kind(&self) -> TermKind { fn kind(&self) -> TermKind {
use Subject::*;
match self { match self {
NamedNode(_) => TermKind::Iri, Self::NamedNode(_) => TermKind::Iri,
BlankNode(_) => TermKind::BlankNode, Self::BlankNode(_) => TermKind::BlankNode,
Self::Triple(_) => panic!("RDF-star is not supported yet by Sophia"),
} }
} }
fn value_raw(&self) -> RawValue<'_> { fn value_raw(&self) -> RawValue<'_> {
use Subject::*;
match self { match self {
NamedNode(n) => n.value_raw(), Self::NamedNode(n) => n.value_raw(),
BlankNode(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 { fn as_dyn(&self) -> &dyn TTerm {
use Subject::*;
match self { match self {
NamedNode(n) => n.as_dyn(), Self::NamedNode(n) => n.as_dyn(),
BlankNode(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> { impl<'a> TTerm for SubjectRef<'a> {
fn kind(&self) -> TermKind { fn kind(&self) -> TermKind {
use SubjectRef::*;
match self { match self {
NamedNode(_) => TermKind::Iri, Self::NamedNode(_) => TermKind::Iri,
BlankNode(_) => TermKind::BlankNode, Self::BlankNode(_) => TermKind::BlankNode,
Self::Triple(_) => panic!("RDF-star is not supported yet by Sophia"),
} }
} }
fn value_raw(&self) -> RawValue<'_> { fn value_raw(&self) -> RawValue<'_> {
use SubjectRef::*;
match self { match self {
NamedNode(n) => n.value_raw(), Self::NamedNode(n) => n.value_raw(),
BlankNode(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 { fn as_dyn(&self) -> &dyn TTerm {
use SubjectRef::*;
match self { match self {
NamedNode(n) => n.as_dyn(), Self::NamedNode(n) => n.as_dyn(),
BlankNode(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 { impl TTerm for Term {
fn kind(&self) -> TermKind { fn kind(&self) -> TermKind {
use Term::*;
match self { match self {
NamedNode(_) => TermKind::Iri, Self::NamedNode(_) => TermKind::Iri,
BlankNode(_) => TermKind::BlankNode, Self::BlankNode(_) => TermKind::BlankNode,
Literal(_) => TermKind::Literal, Self::Literal(_) => TermKind::Literal,
Self::Triple(_) => panic!("RDF-star is not supported yet by Sophia"),
} }
} }
fn value_raw(&self) -> RawValue<'_> { fn value_raw(&self) -> RawValue<'_> {
use Term::*;
match self { match self {
NamedNode(n) => n.value_raw(), Self::NamedNode(n) => n.value_raw(),
BlankNode(n) => n.value_raw(), Self::BlankNode(n) => n.value_raw(),
Literal(l) => l.value_raw(), Self::Literal(l) => l.value_raw(),
Self::Triple(_) => panic!("RDF-star is not supported yet by Sophia"),
} }
} }
fn datatype(&self) -> Option<SimpleIri<'_>> { fn datatype(&self) -> Option<SimpleIri<'_>> {
use Term::*; if let Self::Literal(l) = self {
match self { TTerm::datatype(l)
Literal(l) => TTerm::datatype(l), } else {
_ => None, None
} }
} }
fn language(&self) -> Option<&str> { fn language(&self) -> Option<&str> {
use Term::*; if let Self::Literal(l) = self {
match self { TTerm::language(l)
Literal(l) => TTerm::language(l), } else {
_ => None, None
} }
} }
fn as_dyn(&self) -> &dyn TTerm { fn as_dyn(&self) -> &dyn TTerm {
use Term::*;
match self { match self {
NamedNode(n) => n.as_dyn(), Self::NamedNode(n) => n.as_dyn(),
BlankNode(n) => n.as_dyn(), Self::BlankNode(n) => n.as_dyn(),
Literal(l) => l.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> { impl<'a> TTerm for TermRef<'a> {
fn kind(&self) -> TermKind { fn kind(&self) -> TermKind {
use TermRef::*;
match self { match self {
NamedNode(_) => TermKind::Iri, Self::NamedNode(_) => TermKind::Iri,
BlankNode(_) => TermKind::BlankNode, Self::BlankNode(_) => TermKind::BlankNode,
Literal(_) => TermKind::Literal, Self::Literal(_) => TermKind::Literal,
Self::Triple(_) => panic!("RDF-star is not supported yet by Sophia"),
} }
} }
fn value_raw(&self) -> RawValue<'_> { fn value_raw(&self) -> RawValue<'_> {
use TermRef::*;
match self { match self {
NamedNode(n) => n.value_raw(), Self::NamedNode(n) => n.value_raw(),
BlankNode(n) => n.value_raw(), Self::BlankNode(n) => n.value_raw(),
Literal(l) => l.value_raw(), Self::Literal(l) => l.value_raw(),
Self::Triple(_) => panic!("RDF-star is not supported yet by Sophia"),
} }
} }
fn datatype(&self) -> Option<SimpleIri<'_>> { fn datatype(&self) -> Option<SimpleIri<'_>> {
use TermRef::*; if let Self::Literal(l) = self {
match self { TTerm::datatype(l)
Literal(l) => TTerm::datatype(l), } else {
_ => None, None
} }
} }
fn language(&self) -> Option<&str> { fn language(&self) -> Option<&str> {
use TermRef::*; if let Self::Literal(l) = self {
match self { TTerm::language(l)
Literal(l) => TTerm::language(l), } else {
_ => None, None
} }
} }
fn as_dyn(&self) -> &dyn TTerm { fn as_dyn(&self) -> &dyn TTerm {
use TermRef::*;
match self { match self {
NamedNode(n) => n.as_dyn(), Self::NamedNode(n) => n.as_dyn(),
BlankNode(n) => n.as_dyn(), Self::BlankNode(n) => n.as_dyn(),
Literal(l) => l.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 crate::model::{BlankNodeRef, LiteralRef, NamedNodeRef};
use rio_api::model as rio; use rio_api::model as rio;
use std::fmt; 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)] #[derive(Eq, PartialEq, Debug, Clone, Hash)]
pub enum Subject { pub enum Subject {
NamedNode(NamedNode), NamedNode(NamedNode),
BlankNode(BlankNode), BlankNode(BlankNode),
Triple(Arc<Triple>),
} }
impl Subject { impl Subject {
@ -23,11 +25,17 @@ impl Subject {
self.as_ref().is_blank_node() self.as_ref().is_blank_node()
} }
#[inline]
pub fn is_triple(&self) -> bool {
self.as_ref().is_triple()
}
#[inline] #[inline]
pub fn as_ref(&self) -> SubjectRef<'_> { pub fn as_ref(&self) -> SubjectRef<'_> {
match self { match self {
Self::NamedNode(node) => SubjectRef::NamedNode(node.as_ref()), Self::NamedNode(node) => SubjectRef::NamedNode(node.as_ref()),
Self::BlankNode(node) => SubjectRef::BlankNode(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)] #[derive(Eq, PartialEq, Debug, Clone, Copy, Hash)]
pub enum SubjectRef<'a> { pub enum SubjectRef<'a> {
NamedNode(NamedNodeRef<'a>), NamedNode(NamedNodeRef<'a>),
BlankNode(BlankNodeRef<'a>), BlankNode(BlankNodeRef<'a>),
Triple(&'a Triple),
} }
impl<'a> SubjectRef<'a> { impl<'a> SubjectRef<'a> {
#[inline] #[inline]
pub fn is_named_node(&self) -> bool { pub fn is_named_node(&self) -> bool {
match self { matches!(self, Self::NamedNode(_))
Self::NamedNode(_) => true,
Self::BlankNode(_) => false,
}
} }
#[inline] #[inline]
pub fn is_blank_node(&self) -> bool { pub fn is_blank_node(&self) -> bool {
match self { matches!(self, Self::BlankNode(_))
Self::NamedNode(_) => false,
Self::BlankNode(_) => true,
} }
#[inline]
pub fn is_triple(&self) -> bool {
matches!(self, Self::Triple(_))
} }
#[inline] #[inline]
@ -96,6 +118,7 @@ impl<'a> SubjectRef<'a> {
match self { match self {
Self::NamedNode(node) => Subject::NamedNode(node.into_owned()), Self::NamedNode(node) => Subject::NamedNode(node.into_owned()),
Self::BlankNode(node) => Subject::BlankNode(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 { match self {
Self::NamedNode(node) => node.fmt(f), Self::NamedNode(node) => node.fmt(f),
Self::BlankNode(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> { impl<'a> From<&'a Subject> for SubjectRef<'a> {
#[inline] #[inline]
fn from(node: &'a Subject) -> Self { 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> { impl<'a> From<SubjectRef<'a>> for rio::NamedOrBlankNode<'a> {
#[inline] #[inline]
fn from(node: SubjectRef<'a>) -> Self { fn from(node: SubjectRef<'a>) -> Self {
match node { match node {
SubjectRef::NamedNode(node) => rio::NamedNode::from(node).into(), SubjectRef::NamedNode(node) => rio::NamedNode::from(node).into(),
SubjectRef::BlankNode(node) => rio::BlankNode::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) /// 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)] #[derive(Eq, PartialEq, Debug, Clone, Hash)]
pub enum Term { pub enum Term {
NamedNode(NamedNode), NamedNode(NamedNode),
BlankNode(BlankNode), BlankNode(BlankNode),
Literal(Literal), Literal(Literal),
Triple(Arc<Triple>),
} }
impl Term { impl Term {
@ -187,12 +225,18 @@ impl Term {
self.as_ref().is_literal() self.as_ref().is_literal()
} }
#[inline]
pub fn is_triple(&self) -> bool {
self.as_ref().is_triple()
}
#[inline] #[inline]
pub fn as_ref(&self) -> TermRef<'_> { pub fn as_ref(&self) -> TermRef<'_> {
match self { match self {
Self::NamedNode(node) => TermRef::NamedNode(node.as_ref()), Self::NamedNode(node) => TermRef::NamedNode(node.as_ref()),
Self::BlankNode(node) => TermRef::BlankNode(node.as_ref()), Self::BlankNode(node) => TermRef::BlankNode(node.as_ref()),
Self::Literal(literal) => TermRef::Literal(literal.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() 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 { impl From<Subject> for Term {
#[inline] #[inline]
@ -252,6 +309,7 @@ impl From<Subject> for Term {
match node { match node {
Subject::NamedNode(node) => node.into(), Subject::NamedNode(node) => node.into(),
Subject::BlankNode(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) /// 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)] #[derive(Eq, PartialEq, Debug, Clone, Copy, Hash)]
pub enum TermRef<'a> { pub enum TermRef<'a> {
NamedNode(NamedNodeRef<'a>), NamedNode(NamedNodeRef<'a>),
BlankNode(BlankNodeRef<'a>), BlankNode(BlankNodeRef<'a>),
Literal(LiteralRef<'a>), Literal(LiteralRef<'a>),
Triple(&'a Triple),
} }
impl<'a> TermRef<'a> { impl<'a> TermRef<'a> {
@ -288,12 +347,18 @@ impl<'a> TermRef<'a> {
matches!(self, Self::Literal(_)) matches!(self, Self::Literal(_))
} }
#[inline]
pub fn is_triple(&self) -> bool {
matches!(self, Self::Triple(_))
}
#[inline] #[inline]
pub fn into_owned(self) -> Term { pub fn into_owned(self) -> Term {
match self { match self {
Self::NamedNode(node) => Term::NamedNode(node.into_owned()), Self::NamedNode(node) => Term::NamedNode(node.into_owned()),
Self::BlankNode(node) => Term::BlankNode(node.into_owned()), Self::BlankNode(node) => Term::BlankNode(node.into_owned()),
Self::Literal(literal) => Term::Literal(literal.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 { match self {
Self::NamedNode(node) => node.fmt(f), Self::NamedNode(node) => node.fmt(f),
Self::BlankNode(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> { impl<'a> From<SubjectRef<'a>> for TermRef<'a> {
#[inline] #[inline]
fn from(node: SubjectRef<'a>) -> Self { fn from(node: SubjectRef<'a>) -> Self {
match node { match node {
SubjectRef::NamedNode(node) => node.into(), SubjectRef::NamedNode(node) => node.into(),
SubjectRef::BlankNode(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> { impl<'a> From<TermRef<'a>> for rio::Term<'a> {
#[inline] #[inline]
fn from(node: TermRef<'a>) -> Self { 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::NamedNode(node) => rio::NamedNode::from(node).into(),
TermRef::BlankNode(node) => rio::BlankNode::from(node).into(), TermRef::BlankNode(node) => rio::BlankNode::from(node).into(),
TermRef::Literal(node) => rio::Literal::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. /// 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). /// 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)] #[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> { impl<'a> From<&'a GraphName> for GraphNameRef<'a> {
#[inline] #[inline]
fn from(node: &'a GraphName) -> Self { 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>> { impl<'a> From<GraphNameRef<'a>> for Option<rio::NamedOrBlankNode<'a>> {
#[inline] #[inline]
fn from(name: GraphNameRef<'a>) -> Self { fn from(name: GraphNameRef<'a>) -> Self {

@ -59,18 +59,25 @@ pub fn write_csv_results(
Ok(()) 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() { match term.into() {
TermRef::NamedNode(uri) => sink.write_all(uri.as_str().as_bytes()), TermRef::NamedNode(uri) => sink.write_all(uri.as_str().as_bytes()),
TermRef::BlankNode(bnode) => { TermRef::BlankNode(bnode) => {
sink.write_all(b"_:")?; sink.write_all(b"_:")?;
sink.write_all(bnode.as_str().as_bytes()) 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')) { if s.bytes().any(|c| matches!(c, b'"' | b',' | b'\n' | b'\r')) {
sink.write_all(b"\"")?; sink.write_all(b"\"")?;
for c in s.bytes() { for c in s.bytes() {
@ -138,7 +145,7 @@ pub fn write_tsv_results(
Ok(()) 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 //TODO: full Turtle serialization
match term.into() { match term.into() {
TermRef::NamedNode(node) => write!(sink, "<{}>", node.as_str()), 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()), _ => 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::NamedNode { iri_id } => Some((*iri_id).into()),
EncodedTerm::NumericalBlankNode { .. } EncodedTerm::NumericalBlankNode { .. }
| EncodedTerm::SmallBlankNode { .. } | EncodedTerm::SmallBlankNode { .. }
| EncodedTerm::BigBlankNode { .. } => None, | EncodedTerm::BigBlankNode { .. }
| EncodedTerm::Triple(_) => None,
EncodedTerm::SmallStringLiteral(value) EncodedTerm::SmallStringLiteral(value)
| EncodedTerm::SmallSmallLangStringLiteral { value, .. } | EncodedTerm::SmallSmallLangStringLiteral { value, .. }
| EncodedTerm::SmallBigLangStringLiteral { value, .. } | EncodedTerm::SmallBigLangStringLiteral { value, .. }
@ -2005,6 +2006,17 @@ impl SimpleEvaluator {
_ if b.is_unknown_typed_literal() => None, _ if b.is_unknown_typed_literal() => None,
_ => Some(false), _ => 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::SmallBlankNode { .. }
| EncodedTerm::BigBlankNode { .. } | EncodedTerm::BigBlankNode { .. }
| EncodedTerm::NumericalBlankNode { .. } | EncodedTerm::NumericalBlankNode { .. }
| EncodedTerm::DefaultGraph => None, | EncodedTerm::DefaultGraph
| EncodedTerm::Triple(_) => None,
EncodedTerm::SmallStringLiteral(_) | EncodedTerm::BigStringLiteral { .. } => { EncodedTerm::SmallStringLiteral(_) | EncodedTerm::BigStringLiteral { .. } => {
self.build_named_node(xsd::STRING.as_str()) self.build_named_node(xsd::STRING.as_str())
} }

@ -47,44 +47,59 @@ pub fn write_json_results(
sink.write_all(b",")?; sink.write_all(b",")?;
} }
write_escaped_json_string(variable.as_str(), &mut sink)?; write_escaped_json_string(variable.as_str(), &mut sink)?;
match value { sink.write_all(b":")?;
Term::NamedNode(uri) => { write_json_term(value.as_ref(), &mut sink)?;
sink.write_all(b":{\"type\":\"uri\",\"value\":")?; }
write_escaped_json_string(uri.as_str(), &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"}")?; sink.write_all(b"}")?;
} }
Term::BlankNode(bnode) => { TermRef::BlankNode(bnode) => {
sink.write_all(b":{\"type\":\"bnode\",\"value\":")?; sink.write_all(b"{\"type\":\"bnode\",\"value\":")?;
write_escaped_json_string(bnode.as_str(), &mut sink)?; write_escaped_json_string(bnode.as_str(), sink)?;
sink.write_all(b"}")?; sink.write_all(b"}")?;
} }
Term::Literal(literal) => { TermRef::Literal(literal) => {
sink.write_all(b":{\"type\":\"literal\",\"value\":")?; sink.write_all(b"{\"type\":\"literal\",\"value\":")?;
write_escaped_json_string(literal.value(), &mut sink)?; write_escaped_json_string(literal.value(), sink)?;
if let Some(language) = literal.language() { if let Some(language) = literal.language() {
sink.write_all(b",\"xml:lang\":")?; sink.write_all(b",\"xml:lang\":")?;
write_escaped_json_string(language, &mut sink)?; write_escaped_json_string(language, sink)?;
} else if !literal.is_plain() { } else if !literal.is_plain() {
sink.write_all(b",\"datatype\":")?; 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"}")?; 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(()) 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"\"")?; sink.write_all(b"\"")?;
for c in s.chars() { for c in s.chars() {
match c { match c {

@ -129,7 +129,7 @@ impl<'a> SimpleUpdateEvaluator<'a> {
.into_iter() .into_iter()
.map(|t| { .map(|t| {
Ok(if let Some(t) = 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( self.storage.insert_str(
id, id,
&dataset.get_str(id)?.ok_or_else(|| { &dataset.get_str(id)?.ok_or_else(|| {

@ -88,8 +88,30 @@ fn write_solutions(solutions: QuerySolutionIter, sink: impl Write) -> Result<(),
writer writer
.write_event(Event::Start(binding_tag)) .write_event(Event::Start(binding_tag))
.map_err(map_xml_error)?; .map_err(map_xml_error)?;
match value { write_xml_term(value.as_ref(), &mut writer)?;
Term::NamedNode(uri) => { 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 writer
.write_event(Event::Start(BytesStart::borrowed_name(b"uri"))) .write_event(Event::Start(BytesStart::borrowed_name(b"uri")))
.map_err(map_xml_error)?; .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"))) .write_event(Event::End(BytesEnd::borrowed(b"uri")))
.map_err(map_xml_error)?; .map_err(map_xml_error)?;
} }
Term::BlankNode(bnode) => { TermRef::BlankNode(bnode) => {
writer writer
.write_event(Event::Start(BytesStart::borrowed_name(b"bnode"))) .write_event(Event::Start(BytesStart::borrowed_name(b"bnode")))
.map_err(map_xml_error)?; .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"))) .write_event(Event::End(BytesEnd::borrowed(b"bnode")))
.map_err(map_xml_error)?; .map_err(map_xml_error)?;
} }
Term::Literal(literal) => { TermRef::Literal(literal) => {
let mut literal_tag = BytesStart::borrowed_name(b"literal"); let mut literal_tag = BytesStart::borrowed_name(b"literal");
if let Some(language) = literal.language() { if let Some(language) = literal.language() {
literal_tag.push_attribute(("xml:lang", 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"))) .write_event(Event::End(BytesEnd::borrowed(b"literal")))
.map_err(map_xml_error)?; .map_err(map_xml_error)?;
} }
} TermRef::Triple(triple) => {
writer writer
.write_event(Event::End(BytesEnd::borrowed(b"binding"))) .write_event(Event::Start(BytesStart::borrowed_name(b"triple")))
.map_err(map_xml_error)?; .map_err(map_xml_error)?;
}
writer writer
.write_event(Event::End(BytesEnd::borrowed(b"result"))) .write_event(Event::Start(BytesStart::borrowed_name(b"subject")))
.map_err(map_xml_error)?; .map_err(map_xml_error)?;
} write_xml_term(triple.subject.as_ref().into(), writer)?;
writer writer
.write_event(Event::End(BytesEnd::borrowed(b"results"))) .write_event(Event::End(BytesEnd::borrowed(b"subject")))
.map_err(map_xml_error)?; .map_err(map_xml_error)?;
writer 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)?; .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(()) Ok(())
} }

@ -1,10 +1,11 @@
use crate::error::invalid_data_error; use crate::error::invalid_data_error;
use crate::model::xsd::*; 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 crate::storage::small_string::SmallString;
use std::io; use std::io;
use std::io::{Cursor, Read}; use std::io::{Cursor, Read};
use std::mem::size_of; use std::mem::size_of;
use std::rc::Rc;
pub const LATEST_STORAGE_VERSION: u64 = 1; pub const LATEST_STORAGE_VERSION: u64 = 1;
pub const WRITTEN_TERM_MAX_SIZE: usize = size_of::<u8>() + 2 * size_of::<StrHash>(); 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) // 1-7: usual named nodes (except prefixes c.f. later)
// 8-15: blank nodes // 8-15: blank nodes
// 16-47: literals // 16-47: literals
// 48-64: future use // 48-55: triples
// 56-64: future use
// 64-127: default named node prefixes // 64-127: default named node prefixes
// 128-255: custom named node prefixes // 128-255: custom named node prefixes
const TYPE_NAMED_NODE_ID: u8 = 1; 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_DURATION_LITERAL: u8 = 42;
const TYPE_YEAR_MONTH_DURATION_LITERAL: u8 = 43; const TYPE_YEAR_MONTH_DURATION_LITERAL: u8 = 43;
const TYPE_DAY_TIME_DURATION_LITERAL: u8 = 44; const TYPE_DAY_TIME_DURATION_LITERAL: u8 = 44;
const TYPE_TRIPLE: u8 = 48;
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub enum QuadEncoding { pub enum QuadEncoding {
@ -396,6 +399,11 @@ impl<R: Read> TermReader for R {
DayTimeDuration::from_be_bytes(buffer), 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")), _ => 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.push(TYPE_DAY_TIME_DURATION_LITERAL);
sink.extend_from_slice(&value.to_be_bytes()) 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"), NamedNode::new_unchecked("http://foo.com"),
) )
.into(), .into(),
Triple::new(
NamedNode::new_unchecked("http://foo.com"),
NamedNode::new_unchecked("http://bar.com"),
Literal::from(true),
)
.into(),
]; ];
for term in terms { for term in terms {
let encoded = store.encode_term(term.as_ref()).unwrap(); let encoded = store.encode_term(term.as_ref()).unwrap();

@ -14,6 +14,7 @@ use std::error::Error;
use std::fmt::Debug; use std::fmt::Debug;
use std::hash::Hash; use std::hash::Hash;
use std::hash::Hasher; use std::hash::Hasher;
use std::rc::Rc;
use std::{fmt, io, str}; use std::{fmt, io, str};
#[derive(Eq, PartialEq, Debug, Clone, Copy, Hash)] #[derive(Eq, PartialEq, Debug, Clone, Copy, Hash)]
@ -101,6 +102,7 @@ pub enum EncodedTerm {
DurationLiteral(Duration), DurationLiteral(Duration),
YearMonthDurationLiteral(YearMonthDuration), YearMonthDurationLiteral(YearMonthDuration),
DayTimeDurationLiteral(DayTimeDuration), DayTimeDurationLiteral(DayTimeDuration),
Triple(Rc<EncodedTriple>),
} }
impl PartialEq for EncodedTerm { impl PartialEq for EncodedTerm {
@ -214,6 +216,7 @@ impl PartialEq for EncodedTerm {
(Self::DurationLiteral(a), Self::DurationLiteral(b)) => a == b, (Self::DurationLiteral(a), Self::DurationLiteral(b)) => a == b,
(Self::YearMonthDurationLiteral(a), Self::YearMonthDurationLiteral(b)) => a == b, (Self::YearMonthDurationLiteral(a), Self::YearMonthDurationLiteral(b)) => a == b,
(Self::DayTimeDurationLiteral(a), Self::DayTimeDurationLiteral(b)) => a == b, (Self::DayTimeDurationLiteral(a), Self::DayTimeDurationLiteral(b)) => a == b,
(Self::Triple(a), Self::Triple(b)) => a == b,
(_, _) => false, (_, _) => false,
} }
} }
@ -277,6 +280,7 @@ impl Hash for EncodedTerm {
Self::DurationLiteral(value) => value.hash(state), Self::DurationLiteral(value) => value.hash(state),
Self::YearMonthDurationLiteral(value) => value.hash(state), Self::YearMonthDurationLiteral(value) => value.hash(state),
Self::DayTimeDurationLiteral(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>( pub fn on_each_id<E>(
&self, &self,
mut callback: impl FnMut(&StrHash) -> Result<(), E>, callback: &mut impl FnMut(&StrHash) -> Result<(), E>,
) -> Result<(), E> { ) -> Result<(), E> {
match self { match self {
Self::NamedNode { iri_id } => { Self::NamedNode { iri_id } => {
@ -373,6 +377,11 @@ impl EncodedTerm {
callback(value_id)?; callback(value_id)?;
callback(datatype_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(()) 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)] #[derive(Eq, PartialEq, Debug, Clone, Hash)]
pub struct EncodedQuad { pub struct EncodedQuad {
pub subject: EncodedTerm, pub subject: EncodedTerm,
@ -613,6 +635,9 @@ pub(crate) fn get_encoded_subject(term: SubjectRef<'_>) -> EncodedTerm {
match term { match term {
SubjectRef::NamedNode(named_node) => get_encoded_named_node(named_node), SubjectRef::NamedNode(named_node) => get_encoded_named_node(named_node),
SubjectRef::BlankNode(blank_node) => get_encoded_blank_node(blank_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::NamedNode(named_node) => get_encoded_named_node(named_node),
TermRef::BlankNode(blank_node) => get_encoded_blank_node(blank_node), TermRef::BlankNode(blank_node) => get_encoded_blank_node(blank_node),
TermRef::Literal(literal) => get_encoded_literal(literal), 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 { pub(crate) fn get_encoded_quad(quad: QuadRef<'_>) -> EncodedQuad {
EncodedQuad { EncodedQuad {
subject: get_encoded_subject(quad.subject), subject: get_encoded_subject(quad.subject),
@ -670,6 +706,9 @@ pub(crate) trait WriteEncoder: StrContainer {
match term { match term {
SubjectRef::NamedNode(named_node) => self.encode_named_node(named_node), SubjectRef::NamedNode(named_node) => self.encode_named_node(named_node),
SubjectRef::BlankNode(blank_node) => self.encode_blank_node(blank_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::NamedNode(named_node) => self.encode_named_node(named_node),
TermRef::BlankNode(blank_node) => self.encode_blank_node(blank_node), TermRef::BlankNode(blank_node) => self.encode_blank_node(blank_node),
TermRef::Literal(literal) => self.encode_literal(literal), 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> { fn encode_quad(&self, quad: QuadRef<'_>) -> Result<EncodedQuad, Self::Error> {
Ok(EncodedQuad { Ok(EncodedQuad {
subject: self.encode_subject(quad.subject)?, subject: self.encode_subject(quad.subject)?,
@ -980,7 +1030,10 @@ pub(crate) trait Decoder: StrLookup {
Term::NamedNode(named_node) => Ok(named_node.into()), Term::NamedNode(named_node) => Ok(named_node.into()),
Term::BlankNode(blank_node) => Ok(blank_node.into()), Term::BlankNode(blank_node) => Ok(blank_node.into()),
Term::Literal(_) => Err(DecoderError::Decoder { 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(), msg: "A blank node has been found instead of a named node".to_owned(),
}), }),
Term::Literal(_) => Err(DecoderError::Decoder { 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>> { fn decode_quad(&self, encoded: &EncodedQuad) -> Result<Quad, DecoderError<Self::Error>> {
Ok(Quad::new( Ok(Quad::new(
self.decode_subject(&encoded.subject)?, self.decode_subject(&encoded.subject)?,
self.decode_named_node(&encoded.predicate)?, self.decode_named_node(&encoded.predicate)?,
self.decode_term(&encoded.object)?, self.decode_term(&encoded.object)?,
match &encoded.graph_name { if encoded.graph_name == EncodedTerm::DefaultGraph {
EncodedTerm::DefaultGraph => None, GraphName::DefaultGraph
graph_name => Some(self.decode_subject(graph_name)?), } 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::DurationLiteral(value) => Ok(Literal::from(*value).into()),
EncodedTerm::YearMonthDurationLiteral(value) => Ok(Literal::from(*value).into()), EncodedTerm::YearMonthDurationLiteral(value) => Ok(Literal::from(*value).into()),
EncodedTerm::DayTimeDurationLiteral(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::store::Store;
//! use oxigraph::sparql::QueryResults; //! use oxigraph::sparql::QueryResults;
//! use oxigraph::model::*; //! use oxigraph::model::*;
//! # use std::fs::remove_dir_all;
//! //!
//! # { //! let store = Store::new()?;
//! let store = Store::open("example.db")?;
//! //!
//! // insertion //! // insertion
//! let ex = NamedNode::new("http://example.com")?; //! 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)?; //! store.insert(&quad)?;
//! //!
//! // quad filter //! // quad filter
@ -23,9 +21,6 @@
//! if let QueryResults::Solutions(mut solutions) = store.query("SELECT ?s WHERE { ?s ?p ?o }")? { //! if let QueryResults::Solutions(mut solutions) = store.query("SELECT ?s WHERE { ?s ?p ?o }")? {
//! assert_eq!(solutions.next().unwrap()?.get("s"), Some(&ex.into())); //! assert_eq!(solutions.next().unwrap()?.get("s"), Some(&ex.into()));
//! }; //! };
//! #
//! # };
//! # remove_dir_all("example.db")?;
//! # Result::<_,Box<dyn std::error::Error>>::Ok(()) //! # Result::<_,Box<dyn std::error::Error>>::Ok(())
//! ``` //! ```
@ -69,7 +64,7 @@ use std::{fmt, io, str};
/// ///
/// // insertion /// // insertion
/// let ex = NamedNode::new("http://example.com")?; /// 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)?; /// store.insert(&quad)?;
/// ///
/// // quad filter /// // quad filter
@ -119,7 +114,7 @@ impl Store {
/// ///
/// // insertions /// // insertions
/// let ex = NamedNodeRef::new("http://example.com")?; /// 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 /// // SPARQL query
/// if let QueryResults::Solutions(mut solutions) = store.query("SELECT ?s WHERE { ?s ?p ?o }")? { /// if let QueryResults::Solutions(mut solutions) = store.query("SELECT ?s WHERE { ?s ?p ?o }")? {
@ -154,7 +149,7 @@ impl Store {
/// ///
/// // insertion /// // insertion
/// let ex = NamedNode::new("http://example.com")?; /// 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)?; /// store.insert(&quad)?;
/// ///
/// // quad filter by object /// // quad filter by object
@ -220,7 +215,7 @@ impl Store {
/// ///
/// // we inspect the store contents /// // we inspect the store contents
/// let ex = NamedNodeRef::new("http://example.com").unwrap(); /// 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(()) /// # Result::<_,Box<dyn std::error::Error>>::Ok(())
/// ``` /// ```
pub fn update( pub fn update(
@ -298,7 +293,7 @@ impl Store {
/// ///
/// // we inspect the store contents /// // we inspect the store contents
/// let ex = NamedNodeRef::new("http://example.com")?; /// 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(()) /// # Result::<_,Box<dyn std::error::Error>>::Ok(())
/// ``` /// ```
/// ///
@ -441,12 +436,12 @@ impl Store {
/// Usage example: /// Usage example:
/// ``` /// ```
/// use oxigraph::store::Store; /// use oxigraph::store::Store;
/// use oxigraph::model::{NamedNode, QuadRef, Subject}; /// use oxigraph::model::*;
/// ///
/// let ex = NamedNode::new("http://example.com")?; /// let ex = NamedNode::new("http://example.com")?;
/// let store = Store::new()?; /// let store = Store::new()?;
/// store.insert(QuadRef::new(&ex, &ex, &ex, &ex))?; /// 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<_>,_>>()?); /// assert_eq!(vec![Subject::from(ex)], store.named_graphs().collect::<Result<Vec<_>,_>>()?);
/// # Result::<_,Box<dyn std::error::Error>>::Ok(()) /// # Result::<_,Box<dyn std::error::Error>>::Ok(())
/// ``` /// ```
@ -560,12 +555,12 @@ impl Store {
/// Usage example: /// Usage example:
/// ``` /// ```
/// use oxigraph::store::Store; /// use oxigraph::store::Store;
/// use oxigraph::model::{NamedNodeRef, QuadRef}; /// use oxigraph::model::*;
/// ///
/// let ex = NamedNodeRef::new("http://example.com")?; /// let ex = NamedNodeRef::new("http://example.com")?;
/// let store = Store::new()?; /// let store = Store::new()?;
/// store.insert(QuadRef::new(ex, ex, ex, ex))?; /// 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()); /// assert_eq!(2, store.len());
/// ///
/// store.clear()?; /// store.clear()?;
@ -616,7 +611,7 @@ impl Transaction<'_> {
/// ///
/// // we inspect the store content /// // we inspect the store content
/// let ex = NamedNodeRef::new("http://example.com")?; /// 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(()) /// # 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_o = Term::from(Literal::from(1));
let main_g = GraphName::from(BlankNode::default()); 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( let named_quad = Quad::new(
main_s.clone(), main_s.clone(),
main_p.clone(), main_p.clone(),
@ -777,23 +777,33 @@ fn store() -> Result<(), io::Error> {
main_g.clone(), main_g.clone(),
); );
let default_quads = vec![ 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(), default_quad.clone(),
Quad::new( Quad::new(
main_s.clone(), main_s.clone(),
main_p.clone(), main_p.clone(),
Literal::from(200000000), Literal::from(200000000),
None, GraphName::DefaultGraph,
), ),
]; ];
let all_quads = vec![ 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(), default_quad.clone(),
Quad::new( Quad::new(
main_s.clone(), main_s.clone(),
main_p.clone(), main_p.clone(),
Literal::from(200000000), Literal::from(200000000),
None, GraphName::DefaultGraph,
), ),
named_quad.clone(), named_quad.clone(),
]; ];

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

@ -408,6 +408,7 @@ impl PyObjectProtocol for PyDefaultGraph {
pub enum PySubject { pub enum PySubject {
NamedNode(PyNamedNode), NamedNode(PyNamedNode),
BlankNode(PyBlankNode), BlankNode(PyBlankNode),
Triple(PyTriple),
} }
impl From<PySubject> for Subject { impl From<PySubject> for Subject {
@ -415,6 +416,7 @@ impl From<PySubject> for Subject {
match node { match node {
PySubject::NamedNode(node) => node.into(), PySubject::NamedNode(node) => node.into(),
PySubject::BlankNode(node) => node.into(), PySubject::BlankNode(node) => node.into(),
PySubject::Triple(triple) => triple.into(),
} }
} }
} }
@ -424,6 +426,7 @@ impl From<Subject> for PySubject {
match node { match node {
Subject::NamedNode(node) => PySubject::NamedNode(node.into()), Subject::NamedNode(node) => PySubject::NamedNode(node.into()),
Subject::BlankNode(node) => PySubject::BlankNode(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 { match self {
PySubject::NamedNode(node) => node.into_py(py), PySubject::NamedNode(node) => node.into_py(py),
PySubject::BlankNode(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), NamedNode(PyNamedNode),
BlankNode(PyBlankNode), BlankNode(PyBlankNode),
Literal(PyLiteral), Literal(PyLiteral),
Triple(PyTriple),
} }
impl From<PyTerm> for Term { impl From<PyTerm> for Term {
@ -450,6 +455,7 @@ impl From<PyTerm> for Term {
PyTerm::NamedNode(node) => node.into(), PyTerm::NamedNode(node) => node.into(),
PyTerm::BlankNode(node) => node.into(), PyTerm::BlankNode(node) => node.into(),
PyTerm::Literal(literal) => literal.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::NamedNode(node) => PyTerm::NamedNode(node.into()),
Term::BlankNode(node) => PyTerm::BlankNode(node.into()), Term::BlankNode(node) => PyTerm::BlankNode(node.into()),
Term::Literal(literal) => PyTerm::Literal(literal.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::NamedNode(node) => node.into_py(py),
PyTerm::BlankNode(node) => node.into_py(py), PyTerm::BlankNode(node) => node.into_py(py),
PyTerm::Literal(literal) => literal.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 { impl From<PyTriple> for Triple {
fn from(node: PyTriple) -> Self { fn from(triple: PyTriple) -> Self {
node.inner triple.inner
} }
} }
impl<'a> From<&'a PyTriple> for TripleRef<'a> { impl<'a> From<&'a PyTriple> for TripleRef<'a> {
fn from(node: &'a PyTriple) -> Self { fn from(triple: &'a PyTriple) -> Self {
node.inner.as_ref() 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 { fn __repr__(&self) -> String {
let mut buffer = String::new(); let mut buffer = String::new();
buffer.push_str("<Triple subject="); triple_repr(self.inner.as_ref(), &mut buffer);
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('>');
buffer buffer
} }
@ -1109,6 +1123,7 @@ pub fn term_repr(term: TermRef<'_>, buffer: &mut String) {
TermRef::NamedNode(node) => named_node_repr(node, buffer), TermRef::NamedNode(node) => named_node_repr(node, buffer),
TermRef::BlankNode(node) => blank_node_repr(node, buffer), TermRef::BlankNode(node) => blank_node_repr(node, buffer),
TermRef::Literal(literal) => literal_repr(literal, 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")] #[pyclass(module = "oxigraph")]
pub struct TripleComponentsIter { pub struct TripleComponentsIter {
inner: IntoIter<Term>, inner: IntoIter<Term>,

@ -76,6 +76,31 @@ class TestTriple(unittest.TestCase):
self.assertEqual(t.predicate, NamedNode("http://example.com/p")) self.assertEqual(t.predicate, NamedNode("http://example.com/p"))
self.assertEqual(t.object, NamedNode("http://example.com/o")) 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): def test_mapping(self):
t = Triple( t = Triple(
NamedNode("http://example.com/s"), NamedNode("http://example.com/s"),

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

Loading…
Cancel
Save