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 oxigraph::model::{NamedNode, Quad};
use oxigraph::model::{Dataset, Graph, NamedNode, Quad, Triple};
use oxigraph::{MemoryStore, SledStore};
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);
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) {
let mut group = c.benchmark_group("memory");
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/)
mod blank_node;
pub mod dataset;
pub mod graph;
mod interning;
mod literal;
mod named_node;
mod parser;
@ -12,11 +15,13 @@ mod triple;
pub mod vocab;
pub(crate) mod xsd;
pub use crate::model::blank_node::{BlankNode, BlankNodeIdParseError, BlankNodeRef};
pub use crate::model::literal::{Literal, LiteralRef};
pub use crate::model::named_node::{NamedNode, NamedNodeRef};
pub use crate::model::parser::TermParseError;
pub use crate::model::triple::{
pub use self::blank_node::{BlankNode, BlankNodeIdParseError, BlankNodeRef};
pub use self::dataset::Dataset;
pub use self::graph::Graph;
pub use self::literal::{Literal, LiteralRef};
pub use self::named_node::{NamedNode, NamedNodeRef};
pub use self::parser::TermParseError;
pub use self::triple::{
GraphName, GraphNameRef, NamedOrBlankNode, NamedOrBlankNodeRef, Quad, QuadRef, Term, TermRef,
Triple, TripleRef,
};

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

@ -1,6 +1,6 @@
use anyhow::{anyhow, Result};
use oxigraph::io::{DatasetFormat, GraphFormat};
use oxigraph::model::{GraphName, GraphNameRef};
use oxigraph::model::{Dataset, Graph, GraphNameRef};
use oxigraph::MemoryStore;
use std::fs::File;
use std::io::{BufRead, BufReader, Read};
@ -75,8 +75,54 @@ pub fn load_to_store<'a>(
Ok(())
}
pub fn load_store(url: &str) -> Result<MemoryStore> {
let store = MemoryStore::new();
load_to_store(url, &store, &GraphName::DefaultGraph)?;
Ok(store)
pub fn load_to_graph(url: &str, graph: &mut Graph) -> Result<()> {
if url.ends_with(".nt") {
graph.load(read_file(url)?, GraphFormat::NTriples, Some(url))?
} 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 anyhow::{anyhow, Result};
use oxigraph::model::vocab::*;
use oxigraph::model::*;
use oxigraph::MemoryStore;
use std::fmt;
pub struct Test {
@ -50,7 +49,7 @@ impl fmt::Display for Test {
}
pub struct TestManifest {
graph: MemoryStore,
graph: Graph,
tests_to_do: Vec<Term>,
manifests_to_do: Vec<String>,
}
@ -58,7 +57,7 @@ pub struct TestManifest {
impl TestManifest {
pub fn new<S: ToString>(manifest_urls: impl IntoIterator<Item = S>) -> Self {
Self {
graph: MemoryStore::new(),
graph: Graph::new(),
tests_to_do: Vec::new(),
manifests_to_do: manifest_urls
.into_iter()
@ -74,158 +73,160 @@ impl Iterator for TestManifest {
fn next(&mut self) -> Option<Result<Test>> {
match self.tests_to_do.pop() {
Some(Term::NamedNode(test_node)) => {
let kind = match object_for_subject_predicate(&self.graph, &test_node, rdf::TYPE) {
Some(Term::NamedNode(c)) => c,
let kind = match self
.graph
.object_for_subject_predicate(&test_node, rdf::TYPE)
{
Some(TermRef::NamedNode(c)) => c.into_owned(),
_ => return self.next(), //We ignore the test
};
let name = match object_for_subject_predicate(&self.graph, &test_node, mf::NAME) {
Some(Term::Literal(c)) => Some(c.value().to_string()),
let name = match self
.graph
.object_for_subject_predicate(&test_node, mf::NAME)
{
Some(TermRef::Literal(c)) => Some(c.value().to_string()),
_ => None,
};
let comment =
match object_for_subject_predicate(&self.graph, &test_node, rdfs::COMMENT) {
Some(Term::Literal(c)) => Some(c.value().to_string()),
_ => None,
};
let (action, query, update, data, graph_data, service_data) =
match object_for_subject_predicate(&self.graph, &test_node, mf::ACTION) {
Some(Term::NamedNode(n)) => {
(Some(n.into_string()), None, None, None, vec![], vec![])
}
Some(Term::BlankNode(n)) => {
let query =
match object_for_subject_predicate(&self.graph, &n, qt::QUERY) {
Some(Term::NamedNode(q)) => Some(q.into_string()),
_ => None,
};
let update =
match object_for_subject_predicate(&self.graph, &n, ut::REQUEST) {
Some(Term::NamedNode(q)) => Some(q.into_string()),
_ => None,
};
let data = match object_for_subject_predicate(&self.graph, &n, qt::DATA)
.or_else(|| object_for_subject_predicate(&self.graph, &n, ut::DATA))
{
Some(Term::NamedNode(q)) => Some(q.into_string()),
_ => None,
};
let graph_data =
objects_for_subject_predicate(&self.graph, &n, qt::GRAPH_DATA)
.chain(objects_for_subject_predicate(
&self.graph,
&n,
ut::GRAPH_DATA,
))
.filter_map(|g| match g {
Term::NamedNode(q) => Some((q.clone(), q.into_string())),
Term::BlankNode(node) => {
if let Some(Term::NamedNode(graph)) =
object_for_subject_predicate(
&self.graph,
&node,
ut::GRAPH,
)
{
if let Some(Term::Literal(name)) =
object_for_subject_predicate(
&self.graph,
&node,
rdfs::LABEL,
)
{
Some((
NamedNode::new(name.value()).unwrap(),
graph.into_string(),
))
} 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()))
let comment = match self
.graph
.object_for_subject_predicate(&test_node, rdfs::COMMENT)
{
Some(TermRef::Literal(c)) => Some(c.value().to_string()),
_ => None,
};
let (action, query, update, data, graph_data, service_data) = match self
.graph
.object_for_subject_predicate(&test_node, mf::ACTION)
{
Some(TermRef::NamedNode(n)) => (
Some(n.as_str().to_owned()),
None,
None,
None,
vec![],
vec![],
),
Some(TermRef::BlankNode(n)) => {
let query = match self.graph.object_for_subject_predicate(n, qt::QUERY) {
Some(TermRef::NamedNode(q)) => Some(q.as_str().to_owned()),
_ => None,
};
let update = match self.graph.object_for_subject_predicate(n, ut::REQUEST) {
Some(TermRef::NamedNode(q)) => Some(q.as_str().to_owned()),
_ => None,
};
let data = match self
.graph
.object_for_subject_predicate(n, qt::DATA)
.or_else(|| self.graph.object_for_subject_predicate(n, ut::DATA))
{
Some(TermRef::NamedNode(q)) => Some(q.as_str().to_owned()),
_ => None,
};
let graph_data = self
.graph
.objects_for_subject_predicate(n, qt::GRAPH_DATA)
.chain(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)
{
Some((
NamedNode::new(name.value()).unwrap(),
graph.as_str().to_owned(),
))
} else {
None
Some((graph.into_owned(), graph.as_str().to_owned()))
}
})
.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 object_for_subject_predicate(&self.graph, &test_node, mf::RESULT) {
Some(Term::NamedNode(n)) => (Some(n.into_string()), Vec::new()),
Some(Term::BlankNode(n)) => (
if let Some(Term::NamedNode(result)) =
object_for_subject_predicate(&self.graph, &n, ut::DATA)
{
Some(result.into_string())
} else {
None
},
objects_for_subject_predicate(&self.graph, &n, ut::GRAPH_DATA)
.filter_map(|g| match g {
Term::NamedNode(q) => Some((q.clone(), q.into_string())),
Term::BlankNode(node) => {
if let Some(Term::NamedNode(graph)) =
object_for_subject_predicate(
&self.graph,
&node,
ut::GRAPH,
)
} else {
None
}
}
_ => None,
})
.collect();
let service_data = self
.graph
.objects_for_subject_predicate(n, qt::SERVICE_DATA)
.filter_map(|g| match g {
TermRef::NamedNode(g) => Some(g.into()),
TermRef::BlankNode(g) => Some(g.into()),
_ => None,
})
.filter_map(|g: NamedOrBlankNodeRef<'_>| {
if let (
Some(TermRef::NamedNode(endpoint)),
Some(TermRef::NamedNode(data)),
) = (
self.graph.object_for_subject_predicate(g, qt::ENDPOINT),
self.graph.object_for_subject_predicate(g, qt::DATA),
) {
Some((endpoint.as_str().to_owned(), data.as_str().to_owned()))
} else {
None
}
})
.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)) =
object_for_subject_predicate(
&self.graph,
&node,
rdfs::LABEL,
)
{
Some((
NamedNode::new(name.value()).unwrap(),
graph.into_string(),
))
} else {
Some((graph.clone(), graph.into_string()))
}
Some((
NamedNode::new(name.value()).unwrap(),
graph.as_str().to_owned(),
))
} else {
None
Some((graph.into_owned(), graph.as_str().to_owned()))
}
} else {
None
}
_ => None,
})
.collect(),
),
Some(_) => return Some(Err(anyhow!("invalid result"))),
None => (None, Vec::new()),
};
}
_ => None,
})
.collect(),
),
Some(_) => return Some(Err(anyhow!("invalid result"))),
None => (None, Vec::new()),
};
Some(Ok(Test {
id: test_node,
kind,
@ -247,15 +248,16 @@ impl Iterator for TestManifest {
Some(url) => {
let manifest =
NamedOrBlankNodeRef::from(NamedNodeRef::new(url.as_str()).unwrap());
if let Err(error) =
load_to_store(&url, &self.graph, GraphNameRef::DefaultGraph)
{
if let Err(error) = load_to_graph(&url, &mut self.graph) {
return Some(Err(error));
}
// New manifests
match object_for_subject_predicate(&self.graph, manifest, mf::INCLUDE) {
Some(Term::BlankNode(list)) => {
match self
.graph
.object_for_subject_predicate(manifest, mf::INCLUDE)
{
Some(TermRef::BlankNode(list)) => {
self.manifests_to_do.extend(
RdfListIterator::iter(&self.graph, list.into()).filter_map(
|m| match m {
@ -270,8 +272,11 @@ impl Iterator for TestManifest {
}
// New tests
match object_for_subject_predicate(&self.graph, manifest, mf::ENTRIES) {
Some(Term::BlankNode(list)) => {
match self
.graph
.object_for_subject_predicate(manifest, mf::ENTRIES)
{
Some(TermRef::BlankNode(list)) => {
self.tests_to_do
.extend(RdfListIterator::iter(&self.graph, list.into()));
}
@ -290,12 +295,12 @@ impl Iterator for TestManifest {
}
struct RdfListIterator<'a> {
graph: &'a MemoryStore,
current_node: Option<NamedOrBlankNode>,
graph: &'a Graph,
current_node: Option<NamedOrBlankNodeRef<'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 {
graph,
current_node: Some(root),
@ -307,14 +312,17 @@ impl<'a> Iterator for RdfListIterator<'a> {
type Item = Term;
fn next(&mut self) -> Option<Term> {
match self.current_node.clone() {
match self.current_node {
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 =
match object_for_subject_predicate(&self.graph, &current, rdf::REST) {
Some(Term::NamedNode(n)) if n == rdf::NIL => None,
Some(Term::NamedNode(n)) => Some(n.into()),
Some(Term::BlankNode(n)) => Some(n.into()),
match self.graph.object_for_subject_predicate(current, rdf::REST) {
Some(TermRef::NamedNode(n)) if n == rdf::NIL => None,
Some(TermRef::NamedNode(n)) => Some(n.into()),
Some(TermRef::BlankNode(n)) => Some(n.into()),
_ => None,
};
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::report::{store_diff, TestResult};
use crate::report::{dataset_diff, TestResult};
use anyhow::{anyhow, Result};
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#TestTrigPositiveSyntax"
{
match load_store(action) {
match load_dataset(action) {
Ok(_) => Ok(()),
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#TestXMLNegativeSyntax"
{
match load_store(action) {
match load_dataset(action) {
Ok(_) => Err(anyhow!("File parsed with an error even if it should not",)),
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#TestXMLEval"
{
match load_store(action) {
Ok(actual_graph) => {
match load_dataset(action) {
Ok(mut actual_graph) => {
actual_graph.canonicalize();
if let Some(result) = &test.result {
match load_store(result) {
Ok(expected_graph) => {
if expected_graph.is_isomorphic(&actual_graph) {
match load_dataset(result) {
Ok(mut expected_graph) => {
expected_graph.canonicalize();
if expected_graph == actual_graph {
Ok(())
} else {
Err(anyhow!(
"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 chrono::{DateTime, Utc};
use oxigraph::model::NamedNode;
use oxigraph::MemoryStore;
use oxigraph::model::{Dataset, NamedNode};
use text_diff::{diff, Difference};
#[derive(Debug)]
@ -11,10 +10,10 @@ pub struct TestResult {
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(
&normalize_store_text(expected),
&normalize_store_text(actual),
&normalize_dataset_text(expected),
&normalize_dataset_text(actual),
"\n",
);
let mut ret = String::new();
@ -42,11 +41,8 @@ pub fn store_diff(expected: &MemoryStore, actual: &MemoryStore) -> String {
ret
}
fn normalize_store_text(store: &MemoryStore) -> String {
let mut quads: Vec<_> = store
.quads_for_pattern(None, None, None, None)
.map(|q| q.to_string())
.collect();
fn normalize_dataset_text(store: &Dataset) -> String {
let mut quads: Vec<_> = store.iter().map(|q| q.to_string()).collect();
quads.sort();
quads.join("\n")
}

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