Adds SimpleGraph struct and proper isomorphism implementation

pull/10/head
Tpt 5 years ago
parent 4dee8a9aa2
commit ce1c198552
  1. 1
      lib/Cargo.toml
  2. 156
      lib/src/model/graph.rs
  3. 223
      lib/src/model/isomorphism.rs
  4. 3
      lib/src/model/mod.rs
  5. 158
      lib/src/store/isomorphism.rs
  6. 1
      lib/src/store/mod.rs
  7. 52
      lib/tests/rdf_test_cases.rs
  8. 115
      lib/tests/sparql_test_cases.rs

@ -32,6 +32,7 @@ regex = "1"
rio_api = "0.2"
rio_turtle = "0.2"
rio_xml = "0.2"
permutohedron = "0.2"
[build-dependencies]
peg = "0.5"

@ -0,0 +1,156 @@
use crate::model::isomorphism::are_graphs_isomorphic;
use crate::model::*;
use std::collections::HashSet;
use std::fmt;
use std::iter::FromIterator;
/// Simple data structure [RDF graphs](https://www.w3.org/TR/rdf11-concepts/#dfn-graph).
///
/// It is not done to hold big graphs.
///
/// Usage example:
/// ```
/// use rudf::model::*;
/// use rudf::model::SimpleGraph;
/// use std::str::FromStr;
///
/// let mut graph = SimpleGraph::default();
/// let ex = NamedNode::from_str("http://example.com").unwrap();
/// let triple = Triple::new(ex.clone(), ex.clone(), ex.clone());
/// graph.insert(triple.clone());
/// let results: Vec<Triple> = graph.triples_for_subject(&ex.into()).cloned().collect();
/// assert_eq!(vec![triple], results);
/// ```
#[derive(Eq, PartialEq, Debug, Clone, Default)]
pub struct SimpleGraph {
triples: HashSet<Triple>,
}
impl SimpleGraph {
/// Returns all triples contained by the graph
pub fn iter(&self) -> impl Iterator<Item = &Triple> {
self.triples.iter()
}
pub fn triples_for_subject<'a>(
&'a self,
subject: &'a NamedOrBlankNode,
) -> impl Iterator<Item = &Triple> + 'a {
self.iter().filter(move |t| t.subject() == subject)
}
pub fn objects_for_subject_predicate<'a>(
&'a self,
subject: &'a NamedOrBlankNode,
predicate: &'a NamedNode,
) -> impl Iterator<Item = &Term> + 'a {
self.iter()
.filter(move |t| t.subject() == subject && t.predicate() == predicate)
.map(|t| t.object())
}
pub fn object_for_subject_predicate<'a>(
&'a self,
subject: &'a NamedOrBlankNode,
predicate: &'a NamedNode,
) -> Option<&'a Term> {
self.objects_for_subject_predicate(subject, predicate)
.next()
}
pub fn predicates_for_subject_object<'a>(
&'a self,
subject: &'a NamedOrBlankNode,
object: &'a Term,
) -> impl Iterator<Item = &NamedNode> + 'a {
self.iter()
.filter(move |t| t.subject() == subject && t.object() == object)
.map(|t| t.predicate())
}
pub fn triples_for_predicate<'a>(
&'a self,
predicate: &'a NamedNode,
) -> impl Iterator<Item = &Triple> + 'a {
self.iter().filter(move |t| t.predicate() == predicate)
}
pub fn subjects_for_predicate_object<'a>(
&'a self,
predicate: &'a NamedNode,
object: &'a Term,
) -> impl Iterator<Item = &NamedOrBlankNode> + 'a {
self.iter()
.filter(move |t| t.predicate() == predicate && t.object() == object)
.map(|t| t.subject())
}
pub fn triples_for_object<'a>(
&'a self,
object: &'a Term,
) -> impl Iterator<Item = &Triple> + 'a {
self.iter().filter(move |t| t.object() == object)
}
/// Checks if the graph contains the given triple
pub fn contains(&self, triple: &Triple) -> bool {
self.triples.contains(triple)
}
/// Adds a triple to the graph
pub fn insert(&mut self, triple: Triple) -> bool {
self.triples.insert(triple)
}
/// Removes a concrete triple from the graph
pub fn remove(&mut self, triple: &Triple) -> bool {
self.triples.remove(triple)
}
/// Returns the number of triples in this graph
pub fn len(&self) -> usize {
self.triples.len()
}
/// Checks if this graph contains a triple
pub fn is_empty(&self) -> bool {
self.triples.is_empty()
}
/// Checks if the current graph is [isomorphic](https://www.w3.org/TR/rdf11-concepts/#dfn-graph-isomorphism) with an other one
pub fn is_isomorphic(&self, other: &SimpleGraph) -> bool {
are_graphs_isomorphic(self, other)
}
}
impl IntoIterator for SimpleGraph {
type Item = Triple;
type IntoIter = <HashSet<Triple> as IntoIterator>::IntoIter;
fn into_iter(self) -> Self::IntoIter {
self.triples.into_iter()
}
}
impl FromIterator<Triple> for SimpleGraph {
fn from_iter<I: IntoIterator<Item = Triple>>(iter: I) -> Self {
Self {
triples: HashSet::from_iter(iter),
}
}
}
impl Extend<Triple> for SimpleGraph {
fn extend<I: IntoIterator<Item = Triple>>(&mut self, iter: I) {
self.triples.extend(iter)
}
}
impl fmt::Display for SimpleGraph {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for t in &self.triples {
writeln!(f, "{}", t)?;
}
Ok(())
}
}

@ -0,0 +1,223 @@
use crate::model::*;
use permutohedron::LexicalPermutation;
use std::collections::hash_map::DefaultHasher;
use std::collections::HashSet;
use std::collections::{BTreeSet, HashMap};
use std::hash::Hash;
use std::hash::Hasher;
#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Hash)]
struct SubjectPredicate<'a> {
subject: &'a NamedOrBlankNode,
predicate: &'a NamedNode,
}
#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Hash)]
struct PredicateObject<'a> {
predicate: &'a NamedNode,
object: &'a Term,
}
fn subject_predicates_for_object<'a>(
graph: &'a SimpleGraph,
object: &'a Term,
) -> impl Iterator<Item = SubjectPredicate<'a>> + 'a {
graph.triples_for_object(object).map(|t| SubjectPredicate {
subject: t.subject(),
predicate: t.predicate(),
})
}
fn predicate_objects_for_subject<'a>(
graph: &'a SimpleGraph,
subject: &'a NamedOrBlankNode,
) -> impl Iterator<Item = PredicateObject<'a>> + 'a {
graph.triples_for_subject(subject).map(|t| PredicateObject {
predicate: t.predicate(),
object: t.object(),
})
}
fn hash_blank_nodes<'a>(
bnodes: HashSet<&'a BlankNode>,
graph: &'a SimpleGraph,
) -> HashMap<u64, Vec<&'a BlankNode>> {
let mut bnodes_by_hash = HashMap::default();
// NB: we need to sort the triples to have the same hash
for bnode in bnodes {
let mut hasher = DefaultHasher::new();
{
let subject = NamedOrBlankNode::from(bnode.clone());
let mut po_set: BTreeSet<PredicateObject> = BTreeSet::default();
for po in predicate_objects_for_subject(graph, &subject) {
match &po.object {
Term::BlankNode(_) => (),
_ => {
po_set.insert(po);
}
}
}
for po in po_set {
po.hash(&mut hasher);
}
}
{
let object = Term::from(bnode.clone());
let mut sp_set: BTreeSet<SubjectPredicate> = BTreeSet::default();
for sp in subject_predicates_for_object(graph, &object) {
match &sp.subject {
NamedOrBlankNode::BlankNode(_) => (),
_ => {
sp_set.insert(sp);
}
}
}
for sp in sp_set {
sp.hash(&mut hasher);
}
}
bnodes_by_hash
.entry(hasher.finish())
.or_insert_with(Vec::default)
.push(bnode);
}
bnodes_by_hash
}
fn build_and_check_containment_from_hashes<'a>(
hashes_to_see: &mut Vec<&u64>,
a_bnodes_by_hash: &'a HashMap<u64, Vec<&'a BlankNode>>,
b_bnodes_by_hash: &'a HashMap<u64, Vec<&'a BlankNode>>,
a_to_b_mapping: &mut HashMap<&'a BlankNode, &'a BlankNode>,
a: &SimpleGraph,
b: &SimpleGraph,
) -> bool {
let hash = match hashes_to_see.pop() {
Some(h) => h,
None => return check_is_contained(a_to_b_mapping, a, b),
};
let a_nodes = a_bnodes_by_hash
.get(hash)
.map_or(&[] as &[&BlankNode], |v| v.as_slice());
let b_nodes = b_bnodes_by_hash
.get(hash)
.map_or(&[] as &[&BlankNode], |v| v.as_slice());
if a_nodes.len() != b_nodes.len() {
return false;
}
if a_nodes.len() == 1 {
// Avoid allocation for len == 1
a_to_b_mapping.insert(a_nodes[0], b_nodes[0]);
let result = build_and_check_containment_from_hashes(
hashes_to_see,
a_bnodes_by_hash,
b_bnodes_by_hash,
a_to_b_mapping,
a,
b,
);
a_to_b_mapping.remove(a_nodes[0]);
hashes_to_see.push(hash);
result
} else {
// We compute all the rotations of a_nodes and then zip it with b_nodes to have all the possible pairs (a,b)
let mut a_nodes_rotated = a_nodes.to_vec();
a_nodes_rotated.sort();
loop {
for (a_node, b_node) in a_nodes_rotated.iter().zip(b_nodes.iter()) {
a_to_b_mapping.insert(a_node, b_node);
}
let result = if build_and_check_containment_from_hashes(
hashes_to_see,
a_bnodes_by_hash,
b_bnodes_by_hash,
a_to_b_mapping,
a,
b,
) {
Some(true)
} else if a_nodes_rotated.next_permutation() {
None //keep going
} else {
Some(false) // No more permutation
};
if let Some(result) = result {
for a_node in &a_nodes_rotated {
a_to_b_mapping.remove(a_node);
}
hashes_to_see.push(hash);
return result;
}
}
}
}
fn check_is_contained<'a>(
a_to_b_mapping: &mut HashMap<&'a BlankNode, &'a BlankNode>,
a: &SimpleGraph,
b: &SimpleGraph,
) -> bool {
for t_a in a.iter() {
let subject = if let NamedOrBlankNode::BlankNode(s_a) = &t_a.subject() {
a_to_b_mapping[s_a].clone().into()
} else {
t_a.subject().clone()
};
let predicate = t_a.predicate().clone();
let object = if let Term::BlankNode(o_a) = &t_a.object() {
a_to_b_mapping[o_a].clone().into()
} else {
t_a.object().clone()
};
if !b.contains(&Triple::new(subject, predicate, object)) {
return false;
}
}
true
}
fn graph_blank_nodes(graph: &SimpleGraph) -> HashSet<&BlankNode> {
let mut blank_nodes = HashSet::default();
for t in graph.iter() {
if let NamedOrBlankNode::BlankNode(subject) = t.subject() {
blank_nodes.insert(subject);
}
if let Term::BlankNode(object) = &t.object() {
blank_nodes.insert(object);
}
}
blank_nodes
}
pub fn are_graphs_isomorphic(a: &SimpleGraph, b: &SimpleGraph) -> bool {
if a.len() != b.len() {
return false;
}
let a_bnodes = graph_blank_nodes(a);
let a_bnodes_by_hash = hash_blank_nodes(a_bnodes, a);
let b_bnodes = graph_blank_nodes(b);
let b_bnodes_by_hash = hash_blank_nodes(b_bnodes, b);
// Hashes should have the same size everywhere
if a_bnodes_by_hash.len() != b_bnodes_by_hash.len() {
return false;
}
build_and_check_containment_from_hashes(
&mut a_bnodes_by_hash.keys().collect(),
&a_bnodes_by_hash,
&b_bnodes_by_hash,
&mut HashMap::default(),
a,
b,
)
}

@ -4,6 +4,8 @@
mod blank_node;
mod dataset;
mod graph;
mod isomorphism;
mod language_tag;
mod literal;
mod named_node;
@ -14,6 +16,7 @@ pub use crate::model::blank_node::BlankNode;
pub use crate::model::dataset::Dataset;
pub use crate::model::dataset::Graph;
pub use crate::model::dataset::NamedGraph;
pub use crate::model::graph::SimpleGraph;
pub use crate::model::language_tag::LanguageTag;
pub use crate::model::literal::Literal;
pub use crate::model::named_node::NamedNode;

@ -1,158 +0,0 @@
use crate::model::*;
use crate::Result;
use std::collections::hash_map::DefaultHasher;
use std::collections::BTreeSet;
use std::collections::HashMap;
use std::collections::HashSet;
use std::hash::Hash;
use std::hash::Hasher;
#[derive(Eq, PartialEq, Hash, Ord, PartialOrd)]
struct SubjectPredicate {
subject: NamedOrBlankNode,
predicate: NamedNode,
}
impl SubjectPredicate {
fn new(subject: NamedOrBlankNode, predicate: NamedNode) -> Self {
Self { subject, predicate }
}
}
#[derive(Eq, PartialEq, Hash, Ord, PartialOrd)]
struct PredicateObject {
predicate: NamedNode,
object: Term,
}
impl PredicateObject {
fn new(predicate: NamedNode, object: Term) -> Self {
Self { predicate, object }
}
}
fn subject_predicates_for_object(
graph: &impl Graph,
object: &Term,
) -> Result<impl Iterator<Item = Result<SubjectPredicate>>> {
Ok(graph
.triples_for_object(object)?
.map(|t| t.map(|t| SubjectPredicate::new(t.subject().clone(), t.predicate_owned()))))
}
fn predicate_objects_for_subject(
graph: &impl Graph,
subject: &NamedOrBlankNode,
) -> Result<impl Iterator<Item = Result<PredicateObject>>> {
Ok(graph
.triples_for_subject(subject)?
.map(|t| t.map(|t| PredicateObject::new(t.predicate().clone(), t.object_owned()))))
}
fn hash_blank_nodes(
bnodes: HashSet<BlankNode>,
graph: &impl Graph,
) -> Result<HashMap<u64, Vec<BlankNode>>> {
let mut bnodes_by_hash: HashMap<u64, Vec<BlankNode>> = HashMap::default();
// NB: we need to sort the triples to have the same hash
for bnode in bnodes {
let mut hasher = DefaultHasher::new();
{
let subject = NamedOrBlankNode::from(bnode.clone());
let mut po_set: BTreeSet<PredicateObject> = BTreeSet::default();
for po in predicate_objects_for_subject(graph, &subject)? {
let po = po?;
if !po.object.is_blank_node() {
po_set.insert(po);
}
}
for po in po_set {
po.hash(&mut hasher);
}
}
{
let object = Term::from(bnode.clone());
let mut sp_set: BTreeSet<SubjectPredicate> = BTreeSet::default();
for sp in subject_predicates_for_object(graph, &object)? {
let sp = sp?;
if !sp.subject.is_blank_node() {
sp_set.insert(sp);
}
}
for sp in sp_set {
sp.hash(&mut hasher);
}
}
bnodes_by_hash
.entry(hasher.finish())
.or_insert_with(Vec::default)
.push(bnode);
}
Ok(bnodes_by_hash)
}
pub trait GraphIsomorphism {
/// Checks if two graphs are [isomorphic](https://www.w3.org/TR/rdf11-concepts/#dfn-graph-isomorphism)
fn is_isomorphic(&self, other: &Self) -> Result<bool>;
}
impl<G: Graph> GraphIsomorphism for G {
//TODO: proper isomorphism building
fn is_isomorphic(&self, other: &Self) -> Result<bool> {
if self.len()? != other.len()? {
return Ok(false);
}
let mut self_bnodes: HashSet<BlankNode> = HashSet::default();
let mut other_bnodes: HashSet<BlankNode> = HashSet::default();
for t in self.iter()? {
let t = t?;
if let NamedOrBlankNode::BlankNode(subject) = t.subject() {
self_bnodes.insert(subject.clone());
if let Term::BlankNode(object) = t.object() {
self_bnodes.insert(object.clone());
}
} else if let Term::BlankNode(object) = t.object() {
self_bnodes.insert(object.clone());
} else if !other.contains(&t)? {
return Ok(false);
}
}
for t in other.iter()? {
let t = t?;
if let NamedOrBlankNode::BlankNode(subject) = t.subject() {
other_bnodes.insert(subject.clone());
if let Term::BlankNode(object) = t.object() {
other_bnodes.insert(object.clone());
}
} else if let Term::BlankNode(object) = t.object() {
other_bnodes.insert(object.clone());
} else if !self.contains(&t)? {
return Ok(false);
}
}
let self_bnodes_by_hash = hash_blank_nodes(self_bnodes, self)?;
let other_bnodes_by_hash = hash_blank_nodes(other_bnodes, other)?;
if self_bnodes_by_hash.len() != other_bnodes_by_hash.len() {
return Ok(false);
}
for hash in self_bnodes_by_hash.keys() {
if self_bnodes_by_hash.get(hash).map(|l| l.len())
!= other_bnodes_by_hash.get(hash).map(|l| l.len())
{
return Ok(false);
}
}
Ok(true)
}
}

@ -1,7 +1,6 @@
//! Provides implementations of the `rudf::model::Graph` and `rudf::model::Dataset` traits.
pub(crate) mod encoded;
pub mod isomorphism;
mod memory;
pub(crate) mod numeric_encoder;
#[cfg(feature = "rocksdb")]

@ -6,8 +6,6 @@ use rudf::model::*;
use rudf::rio::ntriples::read_ntriples;
use rudf::rio::turtle::read_turtle;
use rudf::rio::xml::read_rdf_xml;
use rudf::store::isomorphism::GraphIsomorphism;
use rudf::store::MemoryGraph;
use rudf::Result;
use std::fmt;
use std::fs::File;
@ -34,7 +32,7 @@ fn turtle_w3c_testsuite() {
match load_turtle(test.action.clone()) {
Ok(action_graph) => match load_turtle(test.result.clone().unwrap()) {
Ok(result_graph) => assert!(
action_graph.is_isomorphic(&result_graph).unwrap(),
action_graph.is_isomorphic(&result_graph),
"Failure on {}. Expected file:\n{}\nParsed file:\n{}\n",
test,
result_graph,
@ -56,13 +54,10 @@ fn turtle_w3c_testsuite() {
.result
.clone()
.map(|r| load_turtle(r))
.unwrap_or_else(|| Ok(MemoryGraph::default()));
.unwrap_or_else(|| Ok(SimpleGraph::default()));
assert!(
action_graph.is_err()
|| !action_graph
.unwrap()
.is_isomorphic(&result_graph.unwrap())
.unwrap(),
|| !action_graph.unwrap().is_isomorphic(&result_graph.unwrap()),
"Failure on {}",
test
);
@ -109,7 +104,7 @@ fn rdf_xml_w3c_testsuite() -> Result<()> {
match load_rdf_xml(test.action.clone()) {
Ok(action_graph) => match load_ntriples(test.result.clone().unwrap()) {
Ok(result_graph) => assert!(
action_graph.is_isomorphic(&result_graph)?,
action_graph.is_isomorphic(&result_graph),
"Failure on {}. Expected file:\n{}\nParsed file:\n{}\n",
test,
result_graph,
@ -132,15 +127,15 @@ fn rdf_xml_w3c_testsuite() -> Result<()> {
Ok(())
}
fn load_turtle(url: Url) -> Result<MemoryGraph> {
fn load_turtle(url: Url) -> Result<SimpleGraph> {
read_turtle(read_file(&url)?, Some(url))?.collect()
}
fn load_ntriples(url: Url) -> Result<MemoryGraph> {
fn load_ntriples(url: Url) -> Result<SimpleGraph> {
read_ntriples(read_file(&url)?)?.collect()
}
fn load_rdf_xml(url: Url) -> Result<MemoryGraph> {
fn load_rdf_xml(url: Url) -> Result<SimpleGraph> {
read_rdf_xml(read_file(&url)?, Some(url))?.collect()
}
@ -189,7 +184,7 @@ impl fmt::Display for Test {
}
pub struct TestManifest {
graph: MemoryGraph,
graph: SimpleGraph,
tests_to_do: Vec<Term>,
manifests_to_do: Vec<Url>,
}
@ -197,7 +192,7 @@ pub struct TestManifest {
impl TestManifest {
pub fn new(url: Url) -> TestManifest {
Self {
graph: MemoryGraph::default(),
graph: SimpleGraph::default(),
tests_to_do: Vec::default(),
manifests_to_do: vec![url],
}
@ -238,7 +233,6 @@ impl Iterator for TestManifest {
let kind = match self
.graph
.object_for_subject_predicate(&test_subject, &rdf::TYPE)
.unwrap()
{
Some(Term::NamedNode(c)) => match c.as_str().split("#").last() {
Some(k) => k.to_string(),
@ -249,7 +243,6 @@ impl Iterator for TestManifest {
let name = match self
.graph
.object_for_subject_predicate(&test_subject, &mf::NAME)
.unwrap()
{
Some(Term::Literal(c)) => Some(c.value().to_string()),
_ => None,
@ -257,7 +250,6 @@ impl Iterator for TestManifest {
let comment = match self
.graph
.object_for_subject_predicate(&test_subject, &rdfs::COMMENT)
.unwrap()
{
Some(Term::Literal(c)) => Some(c.value().to_string()),
_ => None,
@ -265,7 +257,6 @@ impl Iterator for TestManifest {
let action = match self
.graph
.object_for_subject_predicate(&test_subject, &*mf::ACTION)
.unwrap()
{
Some(Term::NamedNode(n)) => n.as_url().clone(),
Some(_) => return Some(Err(format_err!("invalid action"))),
@ -274,7 +265,6 @@ impl Iterator for TestManifest {
let result = match self
.graph
.object_for_subject_predicate(&test_subject, &*mf::RESULT)
.unwrap()
{
Some(Term::NamedNode(n)) => Some(n.as_url().clone()),
Some(_) => return Some(Err(format_err!("invalid result"))),
@ -295,10 +285,7 @@ impl Iterator for TestManifest {
Some(url) => {
let manifest = NamedOrBlankNode::from(NamedNode::new(url.clone()));
match load_turtle(url) {
Ok(g) => g
.iter()
.unwrap()
.for_each(|g| self.graph.insert(&g.unwrap()).unwrap()),
Ok(g) => self.graph.extend(g.into_iter()),
Err(e) => return Some(Err(e.into())),
}
@ -306,7 +293,6 @@ impl Iterator for TestManifest {
match self
.graph
.object_for_subject_predicate(&manifest, &*mf::INCLUDE)
.unwrap()
{
Some(Term::BlankNode(list)) => {
self.manifests_to_do.extend(
@ -325,7 +311,6 @@ impl Iterator for TestManifest {
match self
.graph
.object_for_subject_predicate(&manifest, &*mf::ENTRIES)
.unwrap()
{
Some(Term::BlankNode(list)) => {
self.tests_to_do.extend(RdfListIterator::iter(
@ -350,13 +335,13 @@ impl Iterator for TestManifest {
}
}
pub struct RdfListIterator<'a, G: Graph> {
graph: &'a G,
pub struct RdfListIterator<'a> {
graph: &'a SimpleGraph,
current_node: Option<NamedOrBlankNode>,
}
impl<'a, G: 'a + Graph> RdfListIterator<'a, G> {
fn iter(graph: &'a G, root: NamedOrBlankNode) -> RdfListIterator<'a, G> {
impl<'a> RdfListIterator<'a> {
fn iter(graph: &'a SimpleGraph, root: NamedOrBlankNode) -> RdfListIterator<'a> {
RdfListIterator {
graph,
current_node: Some(root),
@ -364,7 +349,7 @@ impl<'a, G: 'a + Graph> RdfListIterator<'a, G> {
}
}
impl<'a, G: 'a + Graph> Iterator for RdfListIterator<'a, G> {
impl<'a> Iterator for RdfListIterator<'a> {
type Item = Term;
fn next(&mut self) -> Option<Term> {
@ -372,20 +357,17 @@ impl<'a, G: 'a + Graph> Iterator for RdfListIterator<'a, G> {
Some(current) => {
let result = self
.graph
.object_for_subject_predicate(&current, &rdf::FIRST)
.unwrap()?
.clone();
.object_for_subject_predicate(&current, &rdf::FIRST);
self.current_node = match self
.graph
.object_for_subject_predicate(&current, &rdf::REST)
.unwrap()
{
Some(Term::NamedNode(ref n)) if *n == *rdf::NIL => None,
Some(Term::NamedNode(n)) => Some(n.clone().into()),
Some(Term::BlankNode(n)) => Some(n.clone().into()),
_ => None,
};
Some(result)
result.cloned()
}
None => None,
}

@ -11,9 +11,7 @@ use rudf::sparql::parser::read_sparql_query;
use rudf::sparql::xml_results::read_xml_results;
use rudf::sparql::PreparedQuery;
use rudf::sparql::SparqlDataset;
use rudf::store::isomorphism::GraphIsomorphism;
use rudf::store::MemoryDataset;
use rudf::store::MemoryGraph;
use rudf::Result;
use std::fmt;
use std::fs::File;
@ -184,8 +182,7 @@ fn sparql_w3c_query_evaluation_testsuite() {
load_graph(data.clone())
.unwrap()
.iter()
.unwrap()
.for_each(|triple| dataset_default.insert(&triple.unwrap()).unwrap());
.for_each(|triple| dataset_default.insert(triple).unwrap());
dataset
}
None => MemoryDataset::default(),
@ -197,8 +194,7 @@ fn sparql_w3c_query_evaluation_testsuite() {
load_graph(graph_data.clone())
.unwrap()
.iter()
.unwrap()
.for_each(|triple| named_graph.insert(&triple.unwrap()).unwrap());
.for_each(|triple| named_graph.insert(triple).unwrap());
}
match data.prepare_query(read_file(&test.query).unwrap()) {
Err(error) => assert!(
@ -217,12 +213,11 @@ fn sparql_w3c_query_evaluation_testsuite() {
load_sparql_query_result_graph(test.result.clone().unwrap()).unwrap();
let with_order = expected_graph
.triples_for_predicate(&rs::INDEX)
.unwrap()
.next()
.is_some();
let actual_graph = to_graph(result, with_order).unwrap();
assert!(
actual_graph.is_isomorphic(&expected_graph).unwrap(),
actual_graph.is_isomorphic(&expected_graph),
"Failure on {}.\nExpected file:\n{}\nOutput file:\n{}\nParsed query:\n{}\nData:\n{}\n",
test,
expected_graph,
@ -239,7 +234,7 @@ fn sparql_w3c_query_evaluation_testsuite() {
}
}
fn load_graph(url: Url) -> Result<MemoryGraph> {
fn load_graph(url: Url) -> Result<SimpleGraph> {
if url.as_str().ends_with(".ttl") {
read_turtle(read_file(&url)?, Some(url))?.collect()
} else if url.as_str().ends_with(".rdf") {
@ -253,7 +248,7 @@ fn load_sparql_query(url: Url) -> Result<Query> {
read_sparql_query(read_file(&url)?, Some(url))
}
fn load_sparql_query_result_graph(url: Url) -> Result<MemoryGraph> {
fn load_sparql_query_result_graph(url: Url) -> Result<SimpleGraph> {
if url.as_str().ends_with(".srx") {
to_graph(read_xml_results(read_file(&url)?)?, false)
} else {
@ -322,74 +317,74 @@ mod rs {
}
}
fn to_graph(result: QueryResult<'_>, with_order: bool) -> Result<MemoryGraph> {
fn to_graph(result: QueryResult<'_>, with_order: bool) -> Result<SimpleGraph> {
match result {
QueryResult::Graph(graph) => graph.collect(),
QueryResult::Boolean(value) => {
let graph = MemoryGraph::default();
let mut graph = SimpleGraph::default();
let result_set = BlankNode::default();
graph.insert(&Triple::new(
graph.insert(Triple::new(
result_set.clone(),
rdf::TYPE.clone(),
rs::RESULT_SET.clone(),
))?;
graph.insert(&Triple::new(
));
graph.insert(Triple::new(
result_set.clone(),
rs::BOOLEAN.clone(),
Literal::from(value),
))?;
));
Ok(graph)
}
QueryResult::Bindings(bindings) => {
let graph = MemoryGraph::default();
let mut graph = SimpleGraph::default();
let result_set = BlankNode::default();
graph.insert(&Triple::new(
graph.insert(Triple::new(
result_set.clone(),
rdf::TYPE.clone(),
rs::RESULT_SET.clone(),
))?;
));
let (variables, iter) = bindings.destruct();
for variable in &variables {
graph.insert(&Triple::new(
graph.insert(Triple::new(
result_set.clone(),
rs::RESULT_VARIABLE.clone(),
Literal::new_simple_literal(variable.name()?),
))?;
));
}
for (i, binding_values) in iter.enumerate() {
let binding_values = binding_values?;
let solution = BlankNode::default();
graph.insert(&Triple::new(
graph.insert(Triple::new(
result_set.clone(),
rs::SOLUTION.clone(),
solution.clone(),
))?;
));
for i in 0..variables.len() {
if let Some(ref value) = binding_values[i] {
let binding = BlankNode::default();
graph.insert(&Triple::new(
graph.insert(Triple::new(
solution.clone(),
rs::BINDING.clone(),
binding.clone(),
))?;
graph.insert(&Triple::new(
));
graph.insert(Triple::new(
binding.clone(),
rs::VALUE.clone(),
value.clone(),
))?;
graph.insert(&Triple::new(
));
graph.insert(Triple::new(
binding.clone(),
rs::VARIABLE.clone(),
Literal::new_simple_literal(variables[i].name()?),
))?;
));
}
}
if with_order {
graph.insert(&Triple::new(
graph.insert(Triple::new(
solution.clone(),
rs::INDEX.clone(),
Literal::from((i + 1) as i128),
))?;
));
}
}
Ok(graph)
@ -432,7 +427,7 @@ impl fmt::Display for Test {
}
pub struct TestManifest {
graph: MemoryGraph,
graph: SimpleGraph,
tests_to_do: Vec<Term>,
manifests_to_do: Vec<Url>,
}
@ -440,7 +435,7 @@ pub struct TestManifest {
impl TestManifest {
pub fn new(url: Url) -> TestManifest {
Self {
graph: MemoryGraph::default(),
graph: SimpleGraph::default(),
tests_to_do: Vec::default(),
manifests_to_do: vec![url],
}
@ -499,7 +494,6 @@ impl Iterator for TestManifest {
let kind = match self
.graph
.object_for_subject_predicate(&test_subject, &rdf::TYPE)
.unwrap()
{
Some(Term::NamedNode(c)) => match c.as_str().split("#").last() {
Some(k) => k.to_string(),
@ -510,7 +504,6 @@ impl Iterator for TestManifest {
let name = match self
.graph
.object_for_subject_predicate(&test_subject, &mf::NAME)
.unwrap()
{
Some(Term::Literal(c)) => Some(c.value().to_string()),
_ => None,
@ -518,7 +511,6 @@ impl Iterator for TestManifest {
let comment = match self
.graph
.object_for_subject_predicate(&test_subject, &rdfs::COMMENT)
.unwrap()
{
Some(Term::Literal(c)) => Some(c.value().to_string()),
_ => None,
@ -526,34 +518,24 @@ impl Iterator for TestManifest {
let (query, data, graph_data) = match self
.graph
.object_for_subject_predicate(&test_subject, &*mf::ACTION)
.unwrap()
{
Some(Term::NamedNode(n)) => (n.into(), None, vec![]),
Some(Term::NamedNode(n)) => (n.clone().into(), None, vec![]),
Some(Term::BlankNode(n)) => {
let n = n.into();
let query = match self
.graph
.object_for_subject_predicate(&n, &qt::QUERY)
.unwrap()
{
Some(Term::NamedNode(q)) => q.into(),
let n = n.clone().into();
let query = match self.graph.object_for_subject_predicate(&n, &qt::QUERY) {
Some(Term::NamedNode(q)) => q.clone().into(),
Some(_) => return Some(Err(format_err!("invalid query"))),
None => return Some(Err(format_err!("query not found"))),
};
let data = match self
.graph
.object_for_subject_predicate(&n, &qt::DATA)
.unwrap()
{
Some(Term::NamedNode(q)) => Some(q.into()),
let data = match self.graph.object_for_subject_predicate(&n, &qt::DATA) {
Some(Term::NamedNode(q)) => Some(q.clone().into()),
_ => None,
};
let graph_data = self
.graph
.objects_for_subject_predicate(&n, &qt::GRAPH_DATA)
.unwrap()
.filter_map(|g| match g {
Ok(Term::NamedNode(q)) => Some(q.into()),
Term::NamedNode(q) => Some(q.clone().into()),
_ => None,
})
.collect();
@ -570,9 +552,8 @@ impl Iterator for TestManifest {
let result = match self
.graph
.object_for_subject_predicate(&test_subject, &*mf::RESULT)
.unwrap()
{
Some(Term::NamedNode(n)) => Some(n.into()),
Some(Term::NamedNode(n)) => Some(n.clone().into()),
Some(_) => return Some(Err(format_err!("invalid result"))),
None => None,
};
@ -593,10 +574,7 @@ impl Iterator for TestManifest {
Some(url) => {
let manifest = NamedOrBlankNode::from(NamedNode::new(url.clone()));
match load_graph(url) {
Ok(g) => g
.iter()
.unwrap()
.for_each(|g| self.graph.insert(&g.unwrap()).unwrap()),
Ok(g) => self.graph.extend(g.into_iter()),
Err(e) => return Some(Err(e.into())),
}
@ -604,7 +582,6 @@ impl Iterator for TestManifest {
match self
.graph
.object_for_subject_predicate(&manifest, &*mf::INCLUDE)
.unwrap()
{
Some(Term::BlankNode(list)) => {
self.manifests_to_do.extend(
@ -623,7 +600,6 @@ impl Iterator for TestManifest {
match self
.graph
.object_for_subject_predicate(&manifest, &*mf::ENTRIES)
.unwrap()
{
Some(Term::BlankNode(list)) => {
self.tests_to_do.extend(RdfListIterator::iter(
@ -648,13 +624,13 @@ impl Iterator for TestManifest {
}
}
pub struct RdfListIterator<'a, G: Graph> {
graph: &'a G,
pub struct RdfListIterator<'a> {
graph: &'a SimpleGraph,
current_node: Option<NamedOrBlankNode>,
}
impl<'a, G: 'a + Graph> RdfListIterator<'a, G> {
fn iter(graph: &'a G, root: NamedOrBlankNode) -> RdfListIterator<'a, G> {
impl<'a> RdfListIterator<'a> {
fn iter(graph: &'a SimpleGraph, root: NamedOrBlankNode) -> RdfListIterator<'a> {
RdfListIterator {
graph,
current_node: Some(root),
@ -662,7 +638,7 @@ impl<'a, G: 'a + Graph> RdfListIterator<'a, G> {
}
}
impl<'a, G: 'a + Graph> Iterator for RdfListIterator<'a, G> {
impl<'a> Iterator for RdfListIterator<'a> {
type Item = Term;
fn next(&mut self) -> Option<Term> {
@ -670,20 +646,17 @@ impl<'a, G: 'a + Graph> Iterator for RdfListIterator<'a, G> {
Some(current) => {
let result = self
.graph
.object_for_subject_predicate(&current, &rdf::FIRST)
.unwrap()?
.clone();
.object_for_subject_predicate(&current, &rdf::FIRST);
self.current_node = match self
.graph
.object_for_subject_predicate(&current, &rdf::REST)
.unwrap()
{
Some(Term::NamedNode(ref n)) if *n == *rdf::NIL => None,
Some(Term::NamedNode(n)) => Some(n.clone().into()),
Some(Term::BlankNode(n)) => Some(n.clone().into()),
_ => None,
};
Some(result)
result.cloned()
}
None => None,
}

Loading…
Cancel
Save