Adds simple in memory Graph and Dataset

pull/171/head
Tpt 4 years ago
parent 35fe15f796
commit f75dc6a61d
  1. 43
      lib/benches/store.rs
  2. 1521
      lib/src/model/dataset.rs
  3. 302
      lib/src/model/graph.rs
  4. 344
      lib/src/model/interning.rs
  5. 15
      lib/src/model/mod.rs
  6. 2
      lib/src/store/small_string.rs
  7. 56
      testsuite/src/files.rs
  8. 350
      testsuite/src/manifest.rs
  9. 22
      testsuite/src/parser_evaluator.rs
  10. 16
      testsuite/src/report.rs
  11. 250
      testsuite/src/sparql_evaluator.rs

@ -1,12 +1,51 @@
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Throughput}; use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Throughput};
use oxigraph::model::{NamedNode, Quad}; use oxigraph::model::{Dataset, Graph, NamedNode, Quad, Triple};
use oxigraph::{MemoryStore, SledStore}; use oxigraph::{MemoryStore, SledStore};
use rand::random; use rand::random;
use std::iter::FromIterator;
criterion_group!(store_load, memory_load_bench, sled_load_bench); criterion_group!(
store_load,
graph_load_bench,
dataset_load_bench,
memory_load_bench,
sled_load_bench
);
criterion_main!(store_load); criterion_main!(store_load);
fn graph_load_bench(c: &mut Criterion) {
let mut group = c.benchmark_group("graph");
group.nresamples(10);
group.sample_size(10);
for size in [100, 1_000, 10_000].iter() {
group.throughput(Throughput::Elements(*size as u64));
let triples: Vec<_> = create_quads(*size).into_iter().map(Triple::from).collect();
group.bench_function(BenchmarkId::from_parameter(size), |b| {
b.iter(|| {
Graph::from_iter(triples.iter());
});
});
}
group.finish();
}
fn dataset_load_bench(c: &mut Criterion) {
let mut group = c.benchmark_group("dataset");
group.nresamples(10);
group.sample_size(10);
for size in [100, 1_000, 10_000].iter() {
group.throughput(Throughput::Elements(*size as u64));
let quads = create_quads(*size);
group.bench_function(BenchmarkId::from_parameter(size), |b| {
b.iter(|| {
Dataset::from_iter(quads.iter());
});
});
}
group.finish();
}
fn memory_load_bench(c: &mut Criterion) { fn memory_load_bench(c: &mut Criterion) {
let mut group = c.benchmark_group("memory"); let mut group = c.benchmark_group("memory");
group.nresamples(10); group.nresamples(10);

File diff suppressed because it is too large Load Diff

@ -0,0 +1,302 @@
//! [In-memory implementation](super::Graph) of [RDF graphs](https://www.w3.org/TR/rdf11-concepts/#dfn-graph).
//!
//! Usage example:
//! ```
//! use oxigraph::model::*;
//!
//! let mut graph = Graph::default();
//!
//! // insertion
//! let ex = NamedNodeRef::new("http://example.com")?;
//! let triple = TripleRef::new(ex, ex, ex);
//! graph.insert(triple);
//!
//! // simple filter
//! let results: Vec<_> = graph.triples_for_subject(ex).collect();
//! assert_eq!(vec![triple], results);
//! # Result::<_,Box<dyn std::error::Error>>::Ok(())
//! ```
//!
//! See also [`Dataset`](super::Dataset) if you want to get support of multiple RDF graphs at the same time.
use crate::io::GraphFormat;
use crate::model::dataset::*;
use crate::model::*;
use std::io::{BufRead, Write};
use std::iter::FromIterator;
use std::{fmt, io};
/// An in-memory [RDF graph](https://www.w3.org/TR/rdf11-concepts/#dfn-graph).
///
/// It can accomodate a fairly large number of triples (in the few millions).
/// Beware: it interns the string and does not do any garbage collection yet:
/// if you insert and remove a lot of different terms, memory will grow without any reduction.
///
/// Usage example:
/// ```
/// use oxigraph::model::*;
///
/// let mut graph = Graph::default();
///
/// // insertion
/// let ex = NamedNodeRef::new("http://example.com")?;
/// let triple = TripleRef::new(ex, ex, ex);
/// graph.insert(triple);
///
/// // simple filter
/// let results: Vec<_> = graph.triples_for_subject(ex).collect();
/// assert_eq!(vec![triple], results);
/// # Result::<_,Box<dyn std::error::Error>>::Ok(())
/// ```
#[derive(Debug, Default)]
pub struct Graph {
dataset: Dataset,
}
impl Graph {
/// Creates a new graph
pub fn new() -> Self {
Self::default()
}
fn graph(&self) -> GraphView<'_> {
self.dataset.graph(GraphNameRef::DefaultGraph)
}
fn graph_mut(&mut self) -> GraphViewMut<'_> {
self.dataset.graph_mut(GraphNameRef::DefaultGraph)
}
/// Returns all the triples contained by the graph
pub fn iter(&self) -> Iter<'_> {
Iter {
inner: self.graph().iter(),
}
}
pub fn triples_for_subject<'a, 'b>(
&'a self,
subject: impl Into<NamedOrBlankNodeRef<'b>>,
) -> impl Iterator<Item = TripleRef<'a>> + 'a {
self.graph()
.triples_for_interned_subject(self.dataset.encoded_named_or_blank_node(subject))
}
pub fn objects_for_subject_predicate<'a, 'b>(
&'a self,
subject: impl Into<NamedOrBlankNodeRef<'b>>,
predicate: impl Into<NamedNodeRef<'b>>,
) -> impl Iterator<Item = TermRef<'a>> + 'a {
self.graph().objects_for_interned_subject_predicate(
self.dataset.encoded_named_or_blank_node(subject),
self.dataset.encoded_named_node(predicate),
)
}
pub fn object_for_subject_predicate<'a, 'b>(
&'a self,
subject: impl Into<NamedOrBlankNodeRef<'b>>,
predicate: impl Into<NamedNodeRef<'b>>,
) -> Option<TermRef<'a>> {
self.graph()
.objects_for_subject_predicate(subject, predicate)
.next()
}
pub fn predicates_for_subject_object<'a, 'b>(
&'a self,
subject: impl Into<NamedOrBlankNodeRef<'b>>,
object: impl Into<TermRef<'b>>,
) -> impl Iterator<Item = NamedNodeRef<'a>> + 'a {
self.graph().predicates_for_interned_subject_object(
self.dataset.encoded_named_or_blank_node(subject),
self.dataset.encoded_term(object),
)
}
pub fn triples_for_predicate<'a, 'b>(
&'a self,
predicate: impl Into<NamedNodeRef<'b>>,
) -> impl Iterator<Item = TripleRef<'a>> + 'a {
self.graph()
.triples_for_interned_predicate(self.dataset.encoded_named_node(predicate))
}
pub fn subjects_for_predicate_object<'a, 'b>(
&'a self,
predicate: impl Into<NamedNodeRef<'b>>,
object: impl Into<TermRef<'b>>,
) -> impl Iterator<Item = NamedOrBlankNodeRef<'a>> + 'a {
self.graph().subjects_for_interned_predicate_object(
self.dataset.encoded_named_node(predicate),
self.dataset.encoded_term(object),
)
}
pub fn subject_for_predicate_object<'a, 'b>(
&'a self,
predicate: impl Into<NamedNodeRef<'b>>,
object: impl Into<TermRef<'b>>,
) -> Option<NamedOrBlankNodeRef<'a>> {
self.graph().subject_for_predicate_object(predicate, object)
}
pub fn triples_for_object<'a, 'b>(
&'a self,
object: impl Into<TermRef<'b>>,
) -> impl Iterator<Item = TripleRef<'a>> + 'a {
self.graph()
.triples_for_interned_object(self.dataset.encoded_term(object))
}
/// Checks if the graph contains the given triple
pub fn contains<'a>(&self, triple: impl Into<TripleRef<'a>>) -> bool {
self.graph().contains(triple)
}
/// Returns the number of triples in this graph
pub fn len(&self) -> usize {
self.dataset.len()
}
/// Checks if this graph contains a triple
pub fn is_empty(&self) -> bool {
self.dataset.is_empty()
}
/// Adds a triple to the graph
pub fn insert<'a>(&mut self, triple: impl Into<TripleRef<'a>>) -> bool {
self.graph_mut().insert(triple)
}
/// Removes a concrete triple from the graph
pub fn remove<'a>(&mut self, triple: impl Into<TripleRef<'a>>) -> bool {
self.graph_mut().remove(triple)
}
/// Loads a file into the graph.
///
/// Usage example:
/// ```
/// use oxigraph::model::*;
/// use oxigraph::io::GraphFormat;
///
/// let mut graph = Graph::new();
///
/// // insertion
/// let file = b"<http://example.com> <http://example.com> <http://example.com> .";
/// graph.load(file.as_ref(), GraphFormat::NTriples, None)?;
///
/// // we inspect the store contents
/// let ex = NamedNodeRef::new("http://example.com")?;
/// assert!(graph.contains(TripleRef::new(ex, ex, ex)));
/// # Result::<_,Box<dyn std::error::Error>>::Ok(())
/// ```
///
/// Warning: This functions inserts the triples during the parsing.
/// If the parsing fails in the middle of the file, the triples read before stay in the graph.
///
/// Errors related to parameter validation like the base IRI use the [`InvalidInput`](std::io::ErrorKind::InvalidInput) error kind.
/// Errors related to a bad syntax in the loaded file use the [`InvalidData`](std::io::ErrorKind::InvalidData) or [`UnexpectedEof`](std::io::ErrorKind::UnexpectedEof) error kinds.
pub fn load(
&mut self,
reader: impl BufRead,
format: GraphFormat,
base_iri: Option<&str>,
) -> Result<(), io::Error> {
self.graph_mut().load(reader, format, base_iri)
}
/// Dumps the graph into a file.
///
/// Usage example:
/// ```
/// use oxigraph::io::GraphFormat;
/// use oxigraph::model::Graph;
///
/// let file = "<http://example.com> <http://example.com> <http://example.com> .\n".as_bytes();
///
/// let mut store = Graph::new();
/// store.load(file, GraphFormat::NTriples,None)?;
///
/// let mut buffer = Vec::new();
/// store.dump(&mut buffer, GraphFormat::NTriples)?;
/// assert_eq!(file, buffer.as_slice());
/// # Result::<_,Box<dyn std::error::Error>>::Ok(())
/// ```
pub fn dump(&self, writer: impl Write, format: GraphFormat) -> Result<(), io::Error> {
self.graph().dump(writer, format)
}
/// Applies on the graph the canonicalization process described in
/// [Canonical Forms for Isomorphic and Equivalent RDF Graphs: Algorithms for Leaning and Labelling Blank Nodes, Aidan Hogan, 2017](http://aidanhogan.com/docs/rdf-canonicalisation.pdf)
///
/// Warning: This implementation worst-case complexity is in O(b!) with b the number of blank nodes in the input graphs.
pub fn canonicalize(&mut self) {
self.dataset.canonicalize()
}
}
impl PartialEq for Graph {
fn eq(&self, other: &Self) -> bool {
self.dataset == other.dataset
}
}
impl Eq for Graph {}
impl<'a> IntoIterator for &'a Graph {
type Item = TripleRef<'a>;
type IntoIter = Iter<'a>;
fn into_iter(self) -> Iter<'a> {
self.iter()
}
}
impl FromIterator<Triple> for Graph {
fn from_iter<I: IntoIterator<Item = Triple>>(iter: I) -> Self {
let mut g = Graph::new();
g.extend(iter);
g
}
}
impl<'a, T: Into<TripleRef<'a>>> FromIterator<T> for Graph {
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
let mut g = Graph::new();
g.extend(iter);
g
}
}
impl Extend<Triple> for Graph {
fn extend<I: IntoIterator<Item = Triple>>(&mut self, iter: I) {
self.graph_mut().extend(iter)
}
}
impl<'a, T: Into<TripleRef<'a>>> Extend<T> for Graph {
fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
self.graph_mut().extend(iter)
}
}
impl fmt::Display for Graph {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.graph().fmt(f)
}
}
/// Iterator returned by [`Graph::iter`]
pub struct Iter<'a> {
inner: GraphViewIter<'a>,
}
impl<'a> Iterator for Iter<'a> {
type Item = TripleRef<'a>;
fn next(&mut self) -> Option<TripleRef<'a>> {
self.inner.next()
}
}

@ -0,0 +1,344 @@
//! Interning of RDF elements using Rodeo
use crate::model::*;
use lasso::{Key, Rodeo, Spur};
use std::convert::TryInto;
#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Copy, Hash)]
pub struct InternedNamedNode {
id: Spur,
}
impl InternedNamedNode {
pub fn encoded_into(named_node: NamedNodeRef<'_>, interner: &mut Rodeo) -> Self {
Self {
id: interner.get_or_intern(named_node.as_str()),
}
}
pub fn encoded_from(named_node: NamedNodeRef<'_>, interner: &Rodeo) -> Option<Self> {
Some(Self {
id: interner.get(named_node.as_str())?,
})
}
pub fn decode_from<'a>(&self, interner: &'a Rodeo) -> NamedNodeRef<'a> {
NamedNodeRef::new_unchecked(interner.resolve(&self.id))
}
pub fn first() -> Self {
Self { id: fist_spur() }
}
pub fn next(&self) -> Self {
Self {
id: next_spur(self.id),
}
}
pub fn impossible() -> Self {
Self {
id: impossible_spur(),
}
}
}
#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Copy, Hash)]
pub struct InternedBlankNode {
id: Spur,
}
impl InternedBlankNode {
pub fn encoded_into(blank_node: BlankNodeRef<'_>, interner: &mut Rodeo) -> Self {
Self {
id: interner.get_or_intern(blank_node.as_str()),
}
}
pub fn encoded_from(blank_node: BlankNodeRef<'_>, interner: &Rodeo) -> Option<Self> {
Some(Self {
id: interner.get(blank_node.as_str())?,
})
}
pub fn decode_from<'a>(&self, interner: &'a Rodeo) -> BlankNodeRef<'a> {
BlankNodeRef::new_unchecked(interner.resolve(&self.id))
}
pub fn next(&self) -> Self {
Self {
id: next_spur(self.id),
}
}
}
#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Copy, Hash)]
pub enum InternedLiteral {
String {
value_id: Spur,
},
LanguageTaggedString {
value_id: Spur,
language_id: Spur,
},
TypedLiteral {
value_id: Spur,
datatype: InternedNamedNode,
},
}
impl InternedLiteral {
pub fn encoded_into(literal: LiteralRef<'_>, interner: &mut Rodeo) -> Self {
let value_id = interner.get_or_intern(literal.value());
if literal.is_plain() {
if let Some(language) = literal.language() {
Self::LanguageTaggedString {
value_id,
language_id: interner.get_or_intern(language),
}
} else {
Self::String { value_id }
}
} else {
Self::TypedLiteral {
value_id,
datatype: InternedNamedNode::encoded_into(literal.datatype(), interner),
}
}
}
pub fn encoded_from(literal: LiteralRef<'_>, interner: &Rodeo) -> Option<Self> {
let value_id = interner.get(literal.value())?;
Some(if literal.is_plain() {
if let Some(language) = literal.language() {
Self::LanguageTaggedString {
value_id,
language_id: interner.get(language)?,
}
} else {
Self::String { value_id }
}
} else {
Self::TypedLiteral {
value_id,
datatype: InternedNamedNode::encoded_from(literal.datatype(), interner)?,
}
})
}
pub fn decode_from<'a>(&self, interner: &'a Rodeo) -> LiteralRef<'a> {
match self {
InternedLiteral::String { value_id } => {
LiteralRef::new_simple_literal(interner.resolve(value_id))
}
InternedLiteral::LanguageTaggedString {
value_id,
language_id,
} => LiteralRef::new_language_tagged_literal_unchecked(
interner.resolve(value_id),
interner.resolve(language_id),
),
InternedLiteral::TypedLiteral { value_id, datatype } => LiteralRef::new_typed_literal(
interner.resolve(value_id),
datatype.decode_from(interner),
),
}
}
pub fn next(&self) -> Self {
match self {
Self::String { value_id } => Self::String {
value_id: next_spur(*value_id),
},
Self::LanguageTaggedString {
value_id,
language_id,
} => Self::LanguageTaggedString {
value_id: *value_id,
language_id: next_spur(*language_id),
},
Self::TypedLiteral { value_id, datatype } => Self::TypedLiteral {
value_id: *value_id,
datatype: datatype.next(),
},
}
}
}
#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Copy, Hash)]
pub enum InternedNamedOrBlankNode {
NamedNode(InternedNamedNode),
BlankNode(InternedBlankNode),
}
impl InternedNamedOrBlankNode {
pub fn encoded_into(node: NamedOrBlankNodeRef<'_>, interner: &mut Rodeo) -> Self {
match node {
NamedOrBlankNodeRef::NamedNode(node) => {
Self::NamedNode(InternedNamedNode::encoded_into(node, interner))
}
NamedOrBlankNodeRef::BlankNode(node) => {
Self::BlankNode(InternedBlankNode::encoded_into(node, interner))
}
}
}
pub fn encoded_from(node: NamedOrBlankNodeRef<'_>, interner: &Rodeo) -> Option<Self> {
Some(match node {
NamedOrBlankNodeRef::NamedNode(node) => {
Self::NamedNode(InternedNamedNode::encoded_from(node, interner)?)
}
NamedOrBlankNodeRef::BlankNode(node) => {
Self::BlankNode(InternedBlankNode::encoded_from(node, interner)?)
}
})
}
pub fn decode_from<'a>(&self, interner: &'a Rodeo) -> NamedOrBlankNodeRef<'a> {
match self {
Self::NamedNode(node) => NamedOrBlankNodeRef::NamedNode(node.decode_from(interner)),
Self::BlankNode(node) => NamedOrBlankNodeRef::BlankNode(node.decode_from(interner)),
}
}
pub fn first() -> Self {
Self::NamedNode(InternedNamedNode::first())
}
pub fn next(&self) -> Self {
match self {
Self::NamedNode(node) => Self::NamedNode(node.next()),
Self::BlankNode(node) => Self::BlankNode(node.next()),
}
}
pub fn impossible() -> Self {
Self::NamedNode(InternedNamedNode::impossible())
}
}
#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Copy, Hash)]
pub enum InternedGraphName {
DefaultGraph,
NamedNode(InternedNamedNode),
BlankNode(InternedBlankNode),
}
impl InternedGraphName {
pub fn encoded_into(node: GraphNameRef<'_>, interner: &mut Rodeo) -> Self {
match node {
GraphNameRef::DefaultGraph => Self::DefaultGraph,
GraphNameRef::NamedNode(node) => {
Self::NamedNode(InternedNamedNode::encoded_into(node, interner))
}
GraphNameRef::BlankNode(node) => {
Self::BlankNode(InternedBlankNode::encoded_into(node, interner))
}
}
}
pub fn encoded_from(node: GraphNameRef<'_>, interner: &Rodeo) -> Option<Self> {
Some(match node {
GraphNameRef::DefaultGraph => Self::DefaultGraph,
GraphNameRef::NamedNode(node) => {
Self::NamedNode(InternedNamedNode::encoded_from(node, interner)?)
}
GraphNameRef::BlankNode(node) => {
Self::BlankNode(InternedBlankNode::encoded_from(node, interner)?)
}
})
}
pub fn decode_from<'a>(&self, interner: &'a Rodeo) -> GraphNameRef<'a> {
match self {
Self::DefaultGraph => GraphNameRef::DefaultGraph,
Self::NamedNode(node) => GraphNameRef::NamedNode(node.decode_from(interner)),
Self::BlankNode(node) => GraphNameRef::BlankNode(node.decode_from(interner)),
}
}
pub fn first() -> Self {
Self::DefaultGraph
}
pub fn next(&self) -> Self {
match self {
Self::DefaultGraph => Self::NamedNode(InternedNamedNode::first()),
Self::NamedNode(node) => Self::NamedNode(node.next()),
Self::BlankNode(node) => Self::BlankNode(node.next()),
}
}
pub fn impossible() -> Self {
Self::NamedNode(InternedNamedNode::impossible())
}
}
#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Copy, Hash)]
pub enum InternedTerm {
NamedNode(InternedNamedNode),
BlankNode(InternedBlankNode),
Literal(InternedLiteral),
}
impl InternedTerm {
pub fn encoded_into(term: TermRef<'_>, interner: &mut Rodeo) -> Self {
match term {
TermRef::NamedNode(term) => {
Self::NamedNode(InternedNamedNode::encoded_into(term, interner))
}
TermRef::BlankNode(term) => {
Self::BlankNode(InternedBlankNode::encoded_into(term, interner))
}
TermRef::Literal(term) => Self::Literal(InternedLiteral::encoded_into(term, interner)),
}
}
pub fn encoded_from(term: TermRef<'_>, interner: &Rodeo) -> Option<Self> {
Some(match term {
TermRef::NamedNode(term) => {
Self::NamedNode(InternedNamedNode::encoded_from(term, interner)?)
}
TermRef::BlankNode(term) => {
Self::BlankNode(InternedBlankNode::encoded_from(term, interner)?)
}
TermRef::Literal(term) => Self::Literal(InternedLiteral::encoded_from(term, interner)?),
})
}
pub fn decode_from<'a>(&self, interner: &'a Rodeo) -> TermRef<'a> {
match self {
Self::NamedNode(term) => TermRef::NamedNode(term.decode_from(interner)),
Self::BlankNode(term) => TermRef::BlankNode(term.decode_from(interner)),
Self::Literal(term) => TermRef::Literal(term.decode_from(interner)),
}
}
pub fn first() -> Self {
Self::NamedNode(InternedNamedNode::first())
}
pub fn next(&self) -> Self {
match self {
Self::NamedNode(node) => Self::NamedNode(node.next()),
Self::BlankNode(node) => Self::BlankNode(node.next()),
Self::Literal(node) => Self::Literal(node.next()),
}
}
pub fn impossible() -> Self {
Self::NamedNode(InternedNamedNode::impossible())
}
}
fn fist_spur() -> Spur {
Spur::try_from_usize(0).unwrap()
}
fn next_spur(value: Spur) -> Spur {
Spur::try_from_usize(value.into_usize() + 1).unwrap()
}
fn impossible_spur() -> Spur {
Spur::try_from_usize((u32::max_value() - 10).try_into().unwrap()).unwrap()
}

@ -3,6 +3,9 @@
//! Inspired by [RDF/JS](https://rdf.js.org/data-model-spec/) and [Apache Commons RDF](http://commons.apache.org/proper/commons-rdf/) //! Inspired by [RDF/JS](https://rdf.js.org/data-model-spec/) and [Apache Commons RDF](http://commons.apache.org/proper/commons-rdf/)
mod blank_node; mod blank_node;
pub mod dataset;
pub mod graph;
mod interning;
mod literal; mod literal;
mod named_node; mod named_node;
mod parser; mod parser;
@ -12,11 +15,13 @@ mod triple;
pub mod vocab; pub mod vocab;
pub(crate) mod xsd; pub(crate) mod xsd;
pub use crate::model::blank_node::{BlankNode, BlankNodeIdParseError, BlankNodeRef}; pub use self::blank_node::{BlankNode, BlankNodeIdParseError, BlankNodeRef};
pub use crate::model::literal::{Literal, LiteralRef}; pub use self::dataset::Dataset;
pub use crate::model::named_node::{NamedNode, NamedNodeRef}; pub use self::graph::Graph;
pub use crate::model::parser::TermParseError; pub use self::literal::{Literal, LiteralRef};
pub use crate::model::triple::{ pub use self::named_node::{NamedNode, NamedNodeRef};
pub use self::parser::TermParseError;
pub use self::triple::{
GraphName, GraphNameRef, NamedOrBlankNode, NamedOrBlankNodeRef, Quad, QuadRef, Term, TermRef, GraphName, GraphNameRef, NamedOrBlankNode, NamedOrBlankNodeRef, Quad, QuadRef, Term, TermRef,
Triple, TripleRef, Triple, TripleRef,
}; };

@ -1,6 +1,6 @@
use nom::lib::std::convert::TryFrom;
use std::borrow::Borrow; use std::borrow::Borrow;
use std::cmp::Ordering; use std::cmp::Ordering;
use std::convert::TryFrom;
use std::convert::TryInto; use std::convert::TryInto;
use std::error::Error; use std::error::Error;
use std::fmt; use std::fmt;

@ -1,6 +1,6 @@
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use oxigraph::io::{DatasetFormat, GraphFormat}; use oxigraph::io::{DatasetFormat, GraphFormat};
use oxigraph::model::{GraphName, GraphNameRef}; use oxigraph::model::{Dataset, Graph, GraphNameRef};
use oxigraph::MemoryStore; use oxigraph::MemoryStore;
use std::fs::File; use std::fs::File;
use std::io::{BufRead, BufReader, Read}; use std::io::{BufRead, BufReader, Read};
@ -75,8 +75,54 @@ pub fn load_to_store<'a>(
Ok(()) Ok(())
} }
pub fn load_store(url: &str) -> Result<MemoryStore> { pub fn load_to_graph(url: &str, graph: &mut Graph) -> Result<()> {
let store = MemoryStore::new(); if url.ends_with(".nt") {
load_to_store(url, &store, &GraphName::DefaultGraph)?; graph.load(read_file(url)?, GraphFormat::NTriples, Some(url))?
Ok(store) } else if url.ends_with(".ttl") {
graph.load(read_file(url)?, GraphFormat::Turtle, Some(url))?
} else if url.ends_with(".rdf") {
graph.load(read_file(url)?, GraphFormat::RdfXml, Some(url))?
} else {
return Err(anyhow!("Serialization type not found for {}", url));
}
Ok(())
}
pub fn load_graph(url: &str) -> Result<Graph> {
let mut graph = Graph::new();
load_to_graph(url, &mut graph)?;
Ok(graph)
}
pub fn load_to_dataset<'a>(
url: &str,
dataset: &mut Dataset,
to_graph_name: impl Into<GraphNameRef<'a>>,
) -> Result<()> {
if url.ends_with(".nt") {
dataset
.graph_mut(to_graph_name)
.load(read_file(url)?, GraphFormat::NTriples, Some(url))?
} else if url.ends_with(".ttl") {
dataset
.graph_mut(to_graph_name)
.load(read_file(url)?, GraphFormat::Turtle, Some(url))?
} else if url.ends_with(".rdf") {
dataset
.graph_mut(to_graph_name)
.load(read_file(url)?, GraphFormat::RdfXml, Some(url))?
} else if url.ends_with(".nq") {
dataset.load(read_file(url)?, DatasetFormat::NQuads, Some(url))?
} else if url.ends_with(".trig") {
dataset.load(read_file(url)?, DatasetFormat::TriG, Some(url))?
} else {
return Err(anyhow!("Serialization type not found for {}", url));
}
Ok(())
}
pub fn load_dataset(url: &str) -> Result<Dataset> {
let mut dataset = Dataset::new();
load_to_dataset(url, &mut dataset, GraphNameRef::DefaultGraph)?;
Ok(dataset)
} }

@ -1,9 +1,8 @@
use crate::files::load_to_store; use crate::files::load_to_graph;
use crate::vocab::*; use crate::vocab::*;
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use oxigraph::model::vocab::*; use oxigraph::model::vocab::*;
use oxigraph::model::*; use oxigraph::model::*;
use oxigraph::MemoryStore;
use std::fmt; use std::fmt;
pub struct Test { pub struct Test {
@ -50,7 +49,7 @@ impl fmt::Display for Test {
} }
pub struct TestManifest { pub struct TestManifest {
graph: MemoryStore, graph: Graph,
tests_to_do: Vec<Term>, tests_to_do: Vec<Term>,
manifests_to_do: Vec<String>, manifests_to_do: Vec<String>,
} }
@ -58,7 +57,7 @@ pub struct TestManifest {
impl TestManifest { impl TestManifest {
pub fn new<S: ToString>(manifest_urls: impl IntoIterator<Item = S>) -> Self { pub fn new<S: ToString>(manifest_urls: impl IntoIterator<Item = S>) -> Self {
Self { Self {
graph: MemoryStore::new(), graph: Graph::new(),
tests_to_do: Vec::new(), tests_to_do: Vec::new(),
manifests_to_do: manifest_urls manifests_to_do: manifest_urls
.into_iter() .into_iter()
@ -74,158 +73,160 @@ impl Iterator for TestManifest {
fn next(&mut self) -> Option<Result<Test>> { fn next(&mut self) -> Option<Result<Test>> {
match self.tests_to_do.pop() { match self.tests_to_do.pop() {
Some(Term::NamedNode(test_node)) => { Some(Term::NamedNode(test_node)) => {
let kind = match object_for_subject_predicate(&self.graph, &test_node, rdf::TYPE) { let kind = match self
Some(Term::NamedNode(c)) => c, .graph
.object_for_subject_predicate(&test_node, rdf::TYPE)
{
Some(TermRef::NamedNode(c)) => c.into_owned(),
_ => return self.next(), //We ignore the test _ => return self.next(), //We ignore the test
}; };
let name = match object_for_subject_predicate(&self.graph, &test_node, mf::NAME) { let name = match self
Some(Term::Literal(c)) => Some(c.value().to_string()), .graph
.object_for_subject_predicate(&test_node, mf::NAME)
{
Some(TermRef::Literal(c)) => Some(c.value().to_string()),
_ => None, _ => None,
}; };
let comment = let comment = match self
match object_for_subject_predicate(&self.graph, &test_node, rdfs::COMMENT) { .graph
Some(Term::Literal(c)) => Some(c.value().to_string()), .object_for_subject_predicate(&test_node, rdfs::COMMENT)
_ => None, {
}; Some(TermRef::Literal(c)) => Some(c.value().to_string()),
let (action, query, update, data, graph_data, service_data) = _ => None,
match object_for_subject_predicate(&self.graph, &test_node, mf::ACTION) { };
Some(Term::NamedNode(n)) => { let (action, query, update, data, graph_data, service_data) = match self
(Some(n.into_string()), None, None, None, vec![], vec![]) .graph
} .object_for_subject_predicate(&test_node, mf::ACTION)
Some(Term::BlankNode(n)) => { {
let query = Some(TermRef::NamedNode(n)) => (
match object_for_subject_predicate(&self.graph, &n, qt::QUERY) { Some(n.as_str().to_owned()),
Some(Term::NamedNode(q)) => Some(q.into_string()), None,
_ => None, None,
}; None,
let update = vec![],
match object_for_subject_predicate(&self.graph, &n, ut::REQUEST) { vec![],
Some(Term::NamedNode(q)) => Some(q.into_string()), ),
_ => None, Some(TermRef::BlankNode(n)) => {
}; let query = match self.graph.object_for_subject_predicate(n, qt::QUERY) {
let data = match object_for_subject_predicate(&self.graph, &n, qt::DATA) Some(TermRef::NamedNode(q)) => Some(q.as_str().to_owned()),
.or_else(|| object_for_subject_predicate(&self.graph, &n, ut::DATA)) _ => None,
{ };
Some(Term::NamedNode(q)) => Some(q.into_string()), let update = match self.graph.object_for_subject_predicate(n, ut::REQUEST) {
_ => None, Some(TermRef::NamedNode(q)) => Some(q.as_str().to_owned()),
}; _ => None,
let graph_data = };
objects_for_subject_predicate(&self.graph, &n, qt::GRAPH_DATA) let data = match self
.chain(objects_for_subject_predicate( .graph
&self.graph, .object_for_subject_predicate(n, qt::DATA)
&n, .or_else(|| self.graph.object_for_subject_predicate(n, ut::DATA))
ut::GRAPH_DATA, {
)) Some(TermRef::NamedNode(q)) => Some(q.as_str().to_owned()),
.filter_map(|g| match g { _ => None,
Term::NamedNode(q) => Some((q.clone(), q.into_string())), };
Term::BlankNode(node) => { let graph_data = self
if let Some(Term::NamedNode(graph)) = .graph
object_for_subject_predicate( .objects_for_subject_predicate(n, qt::GRAPH_DATA)
&self.graph, .chain(self.graph.objects_for_subject_predicate(n, ut::GRAPH_DATA))
&node, .filter_map(|g| match g {
ut::GRAPH, TermRef::NamedNode(q) => {
) Some((q.into_owned(), q.as_str().to_owned()))
{ }
if let Some(Term::Literal(name)) = TermRef::BlankNode(node) => {
object_for_subject_predicate( if let Some(TermRef::NamedNode(graph)) =
&self.graph, self.graph.object_for_subject_predicate(node, ut::GRAPH)
&node, {
rdfs::LABEL, if let Some(TermRef::Literal(name)) = self
) .graph
{ .object_for_subject_predicate(node, rdfs::LABEL)
Some(( {
NamedNode::new(name.value()).unwrap(), Some((
graph.into_string(), NamedNode::new(name.value()).unwrap(),
)) graph.as_str().to_owned(),
} else { ))
Some((graph.clone(), graph.into_string()))
}
} else {
None
}
}
_ => None,
})
.collect();
let service_data =
objects_for_subject_predicate(&self.graph, &n, qt::SERVICE_DATA)
.filter_map(|g| match g {
Term::NamedNode(g) => Some(g.into()),
Term::BlankNode(g) => Some(g.into()),
_ => None,
})
.filter_map(|g: NamedOrBlankNode| {
if let (
Some(Term::NamedNode(endpoint)),
Some(Term::NamedNode(data)),
) = (
object_for_subject_predicate(
&self.graph,
&g,
qt::ENDPOINT,
),
object_for_subject_predicate(&self.graph, &g, qt::DATA),
) {
Some((endpoint.into_string(), data.into_string()))
} else { } else {
None Some((graph.into_owned(), graph.as_str().to_owned()))
} }
}) } else {
.collect(); None
(None, query, update, data, graph_data, service_data) }
} }
Some(_) => return Some(Err(anyhow!("invalid action"))), _ => None,
None => { })
return Some(Err(anyhow!("action not found for test {}", test_node))); .collect();
} let service_data = self
}; .graph
let (result, result_graph_data) = .objects_for_subject_predicate(n, qt::SERVICE_DATA)
match object_for_subject_predicate(&self.graph, &test_node, mf::RESULT) { .filter_map(|g| match g {
Some(Term::NamedNode(n)) => (Some(n.into_string()), Vec::new()), TermRef::NamedNode(g) => Some(g.into()),
Some(Term::BlankNode(n)) => ( TermRef::BlankNode(g) => Some(g.into()),
if let Some(Term::NamedNode(result)) = _ => None,
object_for_subject_predicate(&self.graph, &n, ut::DATA) })
{ .filter_map(|g: NamedOrBlankNodeRef<'_>| {
Some(result.into_string()) if let (
} else { Some(TermRef::NamedNode(endpoint)),
None Some(TermRef::NamedNode(data)),
}, ) = (
objects_for_subject_predicate(&self.graph, &n, ut::GRAPH_DATA) self.graph.object_for_subject_predicate(g, qt::ENDPOINT),
.filter_map(|g| match g { self.graph.object_for_subject_predicate(g, qt::DATA),
Term::NamedNode(q) => Some((q.clone(), q.into_string())), ) {
Term::BlankNode(node) => { Some((endpoint.as_str().to_owned(), data.as_str().to_owned()))
if let Some(Term::NamedNode(graph)) = } else {
object_for_subject_predicate( None
&self.graph, }
&node, })
ut::GRAPH, .collect();
) (None, query, update, data, graph_data, service_data)
}
Some(_) => return Some(Err(anyhow!("invalid action"))),
None => {
return Some(Err(anyhow!("action not found for test {}", test_node)));
}
};
let (result, result_graph_data) = match self
.graph
.object_for_subject_predicate(&test_node, mf::RESULT)
{
Some(TermRef::NamedNode(n)) => (Some(n.as_str().to_owned()), Vec::new()),
Some(TermRef::BlankNode(n)) => (
if let Some(TermRef::NamedNode(result)) =
self.graph.object_for_subject_predicate(n, ut::DATA)
{
Some(result.as_str().to_owned())
} else {
None
},
self.graph
.objects_for_subject_predicate(n, ut::GRAPH_DATA)
.filter_map(|g| match g {
TermRef::NamedNode(q) => {
Some((q.into_owned(), q.as_str().to_owned()))
}
TermRef::BlankNode(node) => {
if let Some(TermRef::NamedNode(graph)) =
self.graph.object_for_subject_predicate(node, ut::GRAPH)
{
if let Some(TermRef::Literal(name)) = self
.graph
.object_for_subject_predicate(node, rdfs::LABEL)
{ {
if let Some(Term::Literal(name)) = Some((
object_for_subject_predicate( NamedNode::new(name.value()).unwrap(),
&self.graph, graph.as_str().to_owned(),
&node, ))
rdfs::LABEL,
)
{
Some((
NamedNode::new(name.value()).unwrap(),
graph.into_string(),
))
} else {
Some((graph.clone(), graph.into_string()))
}
} else { } else {
None Some((graph.into_owned(), graph.as_str().to_owned()))
} }
} else {
None
} }
_ => None, }
}) _ => None,
.collect(), })
), .collect(),
Some(_) => return Some(Err(anyhow!("invalid result"))), ),
None => (None, Vec::new()), Some(_) => return Some(Err(anyhow!("invalid result"))),
}; None => (None, Vec::new()),
};
Some(Ok(Test { Some(Ok(Test {
id: test_node, id: test_node,
kind, kind,
@ -247,15 +248,16 @@ impl Iterator for TestManifest {
Some(url) => { Some(url) => {
let manifest = let manifest =
NamedOrBlankNodeRef::from(NamedNodeRef::new(url.as_str()).unwrap()); NamedOrBlankNodeRef::from(NamedNodeRef::new(url.as_str()).unwrap());
if let Err(error) = if let Err(error) = load_to_graph(&url, &mut self.graph) {
load_to_store(&url, &self.graph, GraphNameRef::DefaultGraph)
{
return Some(Err(error)); return Some(Err(error));
} }
// New manifests // New manifests
match object_for_subject_predicate(&self.graph, manifest, mf::INCLUDE) { match self
Some(Term::BlankNode(list)) => { .graph
.object_for_subject_predicate(manifest, mf::INCLUDE)
{
Some(TermRef::BlankNode(list)) => {
self.manifests_to_do.extend( self.manifests_to_do.extend(
RdfListIterator::iter(&self.graph, list.into()).filter_map( RdfListIterator::iter(&self.graph, list.into()).filter_map(
|m| match m { |m| match m {
@ -270,8 +272,11 @@ impl Iterator for TestManifest {
} }
// New tests // New tests
match object_for_subject_predicate(&self.graph, manifest, mf::ENTRIES) { match self
Some(Term::BlankNode(list)) => { .graph
.object_for_subject_predicate(manifest, mf::ENTRIES)
{
Some(TermRef::BlankNode(list)) => {
self.tests_to_do self.tests_to_do
.extend(RdfListIterator::iter(&self.graph, list.into())); .extend(RdfListIterator::iter(&self.graph, list.into()));
} }
@ -290,12 +295,12 @@ impl Iterator for TestManifest {
} }
struct RdfListIterator<'a> { struct RdfListIterator<'a> {
graph: &'a MemoryStore, graph: &'a Graph,
current_node: Option<NamedOrBlankNode>, current_node: Option<NamedOrBlankNodeRef<'a>>,
} }
impl<'a> RdfListIterator<'a> { impl<'a> RdfListIterator<'a> {
fn iter(graph: &'a MemoryStore, root: NamedOrBlankNode) -> RdfListIterator<'a> { fn iter(graph: &'a Graph, root: NamedOrBlankNodeRef<'a>) -> RdfListIterator<'a> {
RdfListIterator { RdfListIterator {
graph, graph,
current_node: Some(root), current_node: Some(root),
@ -307,14 +312,17 @@ impl<'a> Iterator for RdfListIterator<'a> {
type Item = Term; type Item = Term;
fn next(&mut self) -> Option<Term> { fn next(&mut self) -> Option<Term> {
match self.current_node.clone() { match self.current_node {
Some(current) => { Some(current) => {
let result = object_for_subject_predicate(&self.graph, &current, rdf::FIRST); let result = self
.graph
.object_for_subject_predicate(current, rdf::FIRST)
.map(|v| v.into_owned());
self.current_node = self.current_node =
match object_for_subject_predicate(&self.graph, &current, rdf::REST) { match self.graph.object_for_subject_predicate(current, rdf::REST) {
Some(Term::NamedNode(n)) if n == rdf::NIL => None, Some(TermRef::NamedNode(n)) if n == rdf::NIL => None,
Some(Term::NamedNode(n)) => Some(n.into()), Some(TermRef::NamedNode(n)) => Some(n.into()),
Some(Term::BlankNode(n)) => Some(n.into()), Some(TermRef::BlankNode(n)) => Some(n.into()),
_ => None, _ => None,
}; };
result result
@ -323,21 +331,3 @@ impl<'a> Iterator for RdfListIterator<'a> {
} }
} }
} }
fn object_for_subject_predicate<'a>(
store: &MemoryStore,
subject: impl Into<NamedOrBlankNodeRef<'a>>,
predicate: impl Into<NamedNodeRef<'a>>,
) -> Option<Term> {
objects_for_subject_predicate(store, subject, predicate).next()
}
fn objects_for_subject_predicate<'a>(
store: &MemoryStore,
subject: impl Into<NamedOrBlankNodeRef<'a>>,
predicate: impl Into<NamedNodeRef<'a>>,
) -> impl Iterator<Item = Term> {
store
.quads_for_pattern(Some(subject.into()), Some(predicate.into()), None, None)
.map(|t| t.object)
}

@ -1,6 +1,6 @@
use crate::files::load_store; use crate::files::load_dataset;
use crate::manifest::Test; use crate::manifest::Test;
use crate::report::{store_diff, TestResult}; use crate::report::{dataset_diff, TestResult};
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use chrono::Utc; use chrono::Utc;
@ -30,7 +30,7 @@ fn evaluate_parser_test(test: &Test) -> Result<()> {
|| test.kind == "http://www.w3.org/ns/rdftest#TestTurtlePositiveSyntax" || test.kind == "http://www.w3.org/ns/rdftest#TestTurtlePositiveSyntax"
|| test.kind == "http://www.w3.org/ns/rdftest#TestTrigPositiveSyntax" || test.kind == "http://www.w3.org/ns/rdftest#TestTrigPositiveSyntax"
{ {
match load_store(action) { match load_dataset(action) {
Ok(_) => Ok(()), Ok(_) => Ok(()),
Err(e) => Err(anyhow!(format!("Parse error: {}", e))), Err(e) => Err(anyhow!(format!("Parse error: {}", e))),
} }
@ -42,7 +42,7 @@ fn evaluate_parser_test(test: &Test) -> Result<()> {
|| test.kind == "http://www.w3.org/ns/rdftest#TestTrigNegativeEval" || test.kind == "http://www.w3.org/ns/rdftest#TestTrigNegativeEval"
|| test.kind == "http://www.w3.org/ns/rdftest#TestXMLNegativeSyntax" || test.kind == "http://www.w3.org/ns/rdftest#TestXMLNegativeSyntax"
{ {
match load_store(action) { match load_dataset(action) {
Ok(_) => Err(anyhow!("File parsed with an error even if it should not",)), Ok(_) => Err(anyhow!("File parsed with an error even if it should not",)),
Err(_) => Ok(()), Err(_) => Ok(()),
} }
@ -50,17 +50,19 @@ fn evaluate_parser_test(test: &Test) -> Result<()> {
|| test.kind == "http://www.w3.org/ns/rdftest#TestTrigEval" || test.kind == "http://www.w3.org/ns/rdftest#TestTrigEval"
|| test.kind == "http://www.w3.org/ns/rdftest#TestXMLEval" || test.kind == "http://www.w3.org/ns/rdftest#TestXMLEval"
{ {
match load_store(action) { match load_dataset(action) {
Ok(actual_graph) => { Ok(mut actual_graph) => {
actual_graph.canonicalize();
if let Some(result) = &test.result { if let Some(result) = &test.result {
match load_store(result) { match load_dataset(result) {
Ok(expected_graph) => { Ok(mut expected_graph) => {
if expected_graph.is_isomorphic(&actual_graph) { expected_graph.canonicalize();
if expected_graph == actual_graph {
Ok(()) Ok(())
} else { } else {
Err(anyhow!( Err(anyhow!(
"The two files are not isomorphic. Diff:\n{}", "The two files are not isomorphic. Diff:\n{}",
store_diff(&expected_graph, &actual_graph) dataset_diff(&expected_graph, &actual_graph)
)) ))
} }
} }

@ -1,7 +1,6 @@
use anyhow::Result; use anyhow::Result;
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use oxigraph::model::NamedNode; use oxigraph::model::{Dataset, NamedNode};
use oxigraph::MemoryStore;
use text_diff::{diff, Difference}; use text_diff::{diff, Difference};
#[derive(Debug)] #[derive(Debug)]
@ -11,10 +10,10 @@ pub struct TestResult {
pub date: DateTime<Utc>, pub date: DateTime<Utc>,
} }
pub fn store_diff(expected: &MemoryStore, actual: &MemoryStore) -> String { pub fn dataset_diff(expected: &Dataset, actual: &Dataset) -> String {
let (_, changeset) = diff( let (_, changeset) = diff(
&normalize_store_text(expected), &normalize_dataset_text(expected),
&normalize_store_text(actual), &normalize_dataset_text(actual),
"\n", "\n",
); );
let mut ret = String::new(); let mut ret = String::new();
@ -42,11 +41,8 @@ pub fn store_diff(expected: &MemoryStore, actual: &MemoryStore) -> String {
ret ret
} }
fn normalize_store_text(store: &MemoryStore) -> String { fn normalize_dataset_text(store: &Dataset) -> String {
let mut quads: Vec<_> = store let mut quads: Vec<_> = store.iter().map(|q| q.to_string()).collect();
.quads_for_pattern(None, None, None, None)
.map(|q| q.to_string())
.collect();
quads.sort(); quads.sort();
quads.join("\n") quads.join("\n")
} }

@ -1,6 +1,6 @@
use crate::files::*; use crate::files::*;
use crate::manifest::*; use crate::manifest::*;
use crate::report::{store_diff, TestResult}; use crate::report::{dataset_diff, TestResult};
use crate::vocab::*; use crate::vocab::*;
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use chrono::Utc; use chrono::Utc;
@ -223,13 +223,17 @@ fn evaluate_sparql_test(test: &Test) -> Result<()> {
error error
)), )),
Ok(()) => { Ok(()) => {
if store.is_isomorphic(&result_store) { let mut store_dataset: Dataset = store.iter().collect();
store_dataset.canonicalize();
let mut result_store_dataset: Dataset = result_store.iter().collect();
result_store_dataset.canonicalize();
if store_dataset == result_store_dataset {
Ok(()) Ok(())
} else { } else {
Err(anyhow!( Err(anyhow!(
"Failure on {}.\nDiff:\n{}\nParsed update:\n{}\n", "Failure on {}.\nDiff:\n{}\nParsed update:\n{}\n",
test, test,
store_diff(&result_store, &store), dataset_diff(&result_store_dataset, &store_dataset),
Update::parse(&read_file_to_string(update_file)?, Some(update_file)) Update::parse(&read_file_to_string(update_file)?, Some(update_file))
.unwrap(), .unwrap(),
)) ))
@ -259,7 +263,7 @@ fn load_sparql_query_result(url: &str) -> Result<StaticQueryResults> {
false, false,
) )
} else { } else {
Ok(StaticQueryResults::from_dataset(load_store(url)?)) Ok(StaticQueryResults::from_graph(load_graph(url)?))
} }
} }
@ -309,82 +313,56 @@ impl ServiceHandler for StaticServiceHandler {
} }
} }
fn to_dataset(result: QueryResults, with_order: bool) -> Result<MemoryStore> { fn to_graph(result: QueryResults, with_order: bool) -> Result<Graph> {
match result { Ok(match result {
QueryResults::Graph(graph) => Ok(graph QueryResults::Graph(graph) => graph.collect::<Result<Graph, _>>()?,
.map(|t| t.map(|t| t.in_graph(None)))
.collect::<Result<_, _>>()?),
QueryResults::Boolean(value) => { QueryResults::Boolean(value) => {
let store = MemoryStore::new(); let mut graph = Graph::new();
let result_set = BlankNode::default(); let result_set = BlankNode::default();
store.insert(Quad::new( graph.insert(TripleRef::new(&result_set, rdf::TYPE, rs::RESULT_SET));
result_set.clone(), graph.insert(TripleRef::new(
rdf::TYPE, &result_set,
rs::RESULT_SET,
None,
));
store.insert(Quad::new(
result_set,
rs::BOOLEAN, rs::BOOLEAN,
Literal::from(value), &Literal::from(value),
None,
)); ));
Ok(store) graph
} }
QueryResults::Solutions(solutions) => { QueryResults::Solutions(solutions) => {
let store = MemoryStore::new(); let mut graph = Graph::new();
let result_set = BlankNode::default(); let result_set = BlankNode::default();
store.insert(Quad::new( graph.insert(TripleRef::new(&result_set, rdf::TYPE, rs::RESULT_SET));
result_set.clone(),
rdf::TYPE,
rs::RESULT_SET,
None,
));
for variable in solutions.variables() { for variable in solutions.variables() {
store.insert(Quad::new( graph.insert(TripleRef::new(
result_set.clone(), &result_set,
rs::RESULT_VARIABLE, rs::RESULT_VARIABLE,
Literal::new_simple_literal(variable.as_str()), LiteralRef::new_simple_literal(variable.as_str()),
None,
)); ));
} }
for (i, solution) in solutions.enumerate() { for (i, solution) in solutions.enumerate() {
let solution = solution?; let solution = solution?;
let solution_id = BlankNode::default(); let solution_id = BlankNode::default();
store.insert(Quad::new( graph.insert(TripleRef::new(&result_set, rs::SOLUTION, &solution_id));
result_set.clone(),
rs::SOLUTION,
solution_id.clone(),
None,
));
for (variable, value) in solution.iter() { for (variable, value) in solution.iter() {
let binding = BlankNode::default(); let binding = BlankNode::default();
store.insert(Quad::new( graph.insert(TripleRef::new(&solution_id, rs::BINDING, &binding));
solution_id.clone(), graph.insert(TripleRef::new(&binding, rs::VALUE, value));
rs::BINDING, graph.insert(TripleRef::new(
binding.clone(), &binding,
None,
));
store.insert(Quad::new(binding.clone(), rs::VALUE, value.clone(), None));
store.insert(Quad::new(
binding,
rs::VARIABLE, rs::VARIABLE,
Literal::new_simple_literal(variable.as_str()), LiteralRef::new_simple_literal(variable.as_str()),
None,
)); ));
} }
if with_order { if with_order {
store.insert(Quad::new( graph.insert(TripleRef::new(
solution_id, &solution_id,
rs::INDEX, rs::INDEX,
Literal::from((i + 1) as i128), &Literal::from((i + 1) as i128),
None,
)); ));
} }
} }
Ok(store) graph
} }
} })
} }
fn are_query_results_isomorphic( fn are_query_results_isomorphic(
@ -423,7 +401,7 @@ fn are_query_results_isomorphic(
expected == actual expected == actual
} }
(StaticQueryResults::Graph(expected), StaticQueryResults::Graph(actual)) => { (StaticQueryResults::Graph(expected), StaticQueryResults::Graph(actual)) => {
expected.is_isomorphic(&actual) expected == actual
} }
_ => false, _ => false,
} }
@ -445,7 +423,7 @@ fn compare_solutions(expected: &[(Variable, Term)], actual: &[(Variable, Term)])
} }
enum StaticQueryResults { enum StaticQueryResults {
Graph(MemoryStore), Graph(Graph),
Solutions { Solutions {
variables: Vec<Variable>, variables: Vec<Variable>,
solutions: Vec<Vec<(Variable, Term)>>, solutions: Vec<Vec<(Variable, Term)>>,
@ -483,83 +461,80 @@ impl fmt::Display for StaticQueryResults {
impl StaticQueryResults { impl StaticQueryResults {
fn from_query_results(results: QueryResults, with_order: bool) -> Result<StaticQueryResults> { fn from_query_results(results: QueryResults, with_order: bool) -> Result<StaticQueryResults> {
Ok(Self::from_dataset(to_dataset(results, with_order)?)) Ok(Self::from_graph(to_graph(results, with_order)?))
} }
fn from_dataset(dataset: MemoryStore) -> StaticQueryResults { fn from_graph(graph: Graph) -> StaticQueryResults {
if let Some(result_set) = dataset // Hack to normalize literals
.quads_for_pattern(None, Some(rdf::TYPE), Some(rs::RESULT_SET.into()), None) let mut graph: Graph = graph
.map(|q| q.subject) .iter()
.next() .map(|t| t.into_owned().in_graph(GraphName::DefaultGraph))
{ .collect::<MemoryStore>()
if let Some(bool) = object_for_subject_predicate(&dataset, &result_set, rs::BOOLEAN) { .into_iter()
.map(Triple::from)
.collect();
if let Some(result_set) = graph.subject_for_predicate_object(rdf::TYPE, rs::RESULT_SET) {
if let Some(bool) = graph.object_for_subject_predicate(result_set, rs::BOOLEAN) {
// Boolean query // Boolean query
StaticQueryResults::Boolean(bool == Literal::from(true).into()) StaticQueryResults::Boolean(bool == Literal::from(true).as_ref().into())
} else { } else {
// Regular query // Regular query
let mut variables: Vec<Variable> = let mut variables: Vec<Variable> = graph
objects_for_subject_predicate(&dataset, &result_set, rs::RESULT_VARIABLE) .objects_for_subject_predicate(result_set, rs::RESULT_VARIABLE)
.filter_map(|object| { .filter_map(|object| {
if let Term::Literal(l) = object { if let TermRef::Literal(l) = object {
Some(Variable::new_unchecked(l.value())) Some(Variable::new_unchecked(l.value()))
} else { } else {
None None
} }
}) })
.collect(); .collect();
variables.sort(); variables.sort();
let mut solutions: Vec<_> = let mut solutions: Vec<_> = graph
objects_for_subject_predicate(&dataset, &result_set, rs::SOLUTION) .objects_for_subject_predicate(result_set, rs::SOLUTION)
.filter_map(|object| { .filter_map(|object| {
if let Term::BlankNode(solution) = object { if let TermRef::BlankNode(solution) = object {
let mut bindings = let mut bindings = graph
objects_for_subject_predicate(&dataset, &solution, rs::BINDING) .objects_for_subject_predicate(solution, rs::BINDING)
.filter_map(|object| { .filter_map(|object| {
if let Term::BlankNode(binding) = object { if let TermRef::BlankNode(binding) = object {
if let ( if let (Some(TermRef::Literal(variable)), Some(value)) = (
Some(Term::Literal(variable)), graph.object_for_subject_predicate(
Some(value), binding,
) = ( rs::VARIABLE,
object_for_subject_predicate( ),
&dataset, graph.object_for_subject_predicate(binding, rs::VALUE),
&binding, ) {
rs::VARIABLE, Some((
), Variable::new_unchecked(variable.value()),
object_for_subject_predicate( value.into_owned(),
&dataset, ))
&binding, } else {
rs::VALUE, None
), }
) { } else {
Some(( None
Variable::new_unchecked(variable.value()), }
value, })
)) .collect::<Vec<_>>();
} else { bindings.sort_by(|(a, _), (b, _)| a.cmp(&b));
None let index = graph
} .object_for_subject_predicate(solution, rs::INDEX)
} else { .and_then(|object| {
None if let TermRef::Literal(l) = object {
} u64::from_str(l.value()).ok()
}) } else {
.collect::<Vec<_>>(); None
bindings.sort_by(|(a, _), (b, _)| a.cmp(&b)); }
let index = });
object_for_subject_predicate(&dataset, &solution, rs::INDEX) Some((bindings, index))
.and_then(|object| { } else {
if let Term::Literal(l) = object { None
u64::from_str(l.value()).ok() }
} else { })
None .collect();
}
});
Some((bindings, index))
} else {
None
}
})
.collect();
solutions.sort_by(|(_, index_a), (_, index_b)| index_a.cmp(index_b)); solutions.sort_by(|(_, index_a), (_, index_b)| index_a.cmp(index_b));
let ordered = solutions.iter().all(|(_, index)| index.is_some()); let ordered = solutions.iter().all(|(_, index)| index.is_some());
@ -574,25 +549,8 @@ impl StaticQueryResults {
} }
} }
} else { } else {
StaticQueryResults::Graph(dataset) graph.canonicalize();
StaticQueryResults::Graph(graph)
} }
} }
} }
fn object_for_subject_predicate<'a>(
store: &MemoryStore,
subject: impl Into<NamedOrBlankNodeRef<'a>>,
predicate: impl Into<NamedNodeRef<'a>>,
) -> Option<Term> {
objects_for_subject_predicate(store, subject, predicate).next()
}
fn objects_for_subject_predicate<'a>(
store: &MemoryStore,
subject: impl Into<NamedOrBlankNodeRef<'a>>,
predicate: impl Into<NamedNodeRef<'a>>,
) -> impl Iterator<Item = Term> {
store
.quads_for_pattern(Some(subject.into()), Some(predicate.into()), None, None)
.map(|t| t.object)
}

Loading…
Cancel
Save