Merge pull request #11 from dwhitney/service-query-options

Service query options
pull/14/head
Thomas Tanon 5 years ago committed by GitHub
commit 033ee373d2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      lib/src/lib.rs
  2. 21
      lib/src/repository.rs
  3. 524
      lib/src/sparql/eval.rs
  4. 63
      lib/src/sparql/mod.rs
  5. 2
      lib/src/sparql/model.rs
  6. 17
      lib/src/sparql/plan.rs
  7. 16
      lib/src/sparql/plan_builder.rs
  8. 5
      lib/src/store/memory.rs
  9. 16
      lib/src/store/mod.rs
  10. 5
      lib/src/store/rocksdb.rs
  11. 240
      lib/tests/service_test_cases.rs
  12. 4
      lib/tests/sparql_test_cases.rs
  13. 5
      server/src/main.rs

@ -27,8 +27,9 @@
//! assert_eq!(vec![quad], results.unwrap()); //! assert_eq!(vec![quad], results.unwrap());
//! //!
//! // SPARQL query //! // SPARQL query
//! let prepared_query = connection.prepare_query("SELECT ?s WHERE { ?s ?p ?o }", QueryOptions::default()).unwrap(); //! let prepared_query = connection.prepare_query("SELECT ?s WHERE { ?s ?p ?o }", None).unwrap();
//! let results = prepared_query.exec().unwrap(); //! let options = QueryOptions::default();
//! let results = prepared_query.exec(&options).unwrap();
//! if let QueryResult::Bindings(results) = results { //! if let QueryResult::Bindings(results) = results {
//! assert_eq!(results.into_values_iter().next().unwrap().unwrap()[0], Some(ex.into())); //! assert_eq!(results.into_values_iter().next().unwrap().unwrap()[0], Some(ex.into()));
//! } //! }

@ -1,5 +1,5 @@
use crate::model::*; use crate::model::*;
use crate::sparql::{PreparedQuery, QueryOptions}; use crate::sparql::{GraphPattern, PreparedQuery};
use crate::{DatasetSyntax, GraphSyntax, Result}; use crate::{DatasetSyntax, GraphSyntax, Result};
use std::io::BufRead; use std::io::BufRead;
@ -30,8 +30,9 @@ use std::io::BufRead;
/// assert_eq!(vec![quad], results.unwrap()); /// assert_eq!(vec![quad], results.unwrap());
/// ///
/// // SPARQL query /// // SPARQL query
/// let prepared_query = connection.prepare_query("SELECT ?s WHERE { ?s ?p ?o }", QueryOptions::default()).unwrap(); /// let prepared_query = connection.prepare_query("SELECT ?s WHERE { ?s ?p ?o }", None).unwrap();
/// let results = prepared_query.exec().unwrap(); /// let options = QueryOptions::default();
/// let results = prepared_query.exec(&options).unwrap();
/// if let QueryResult::Bindings(results) = results { /// if let QueryResult::Bindings(results) = results {
/// assert_eq!(results.into_values_iter().next().unwrap().unwrap()[0], Some(ex.into())); /// assert_eq!(results.into_values_iter().next().unwrap().unwrap()[0], Some(ex.into()));
/// } /// }
@ -75,13 +76,21 @@ pub trait RepositoryConnection: Clone {
/// connection.insert(&Quad::new(ex.clone(), ex.clone(), ex.clone(), None)); /// connection.insert(&Quad::new(ex.clone(), ex.clone(), ex.clone(), None));
/// ///
/// // SPARQL query /// // SPARQL query
/// let prepared_query = connection.prepare_query("SELECT ?s WHERE { ?s ?p ?o }", QueryOptions::default()).unwrap(); /// let prepared_query = connection.prepare_query("SELECT ?s WHERE { ?s ?p ?o }", None).unwrap();
/// let results = prepared_query.exec().unwrap(); /// let options = QueryOptions::default();
/// let results = prepared_query.exec(&options).unwrap();
/// if let QueryResult::Bindings(results) = results { /// if let QueryResult::Bindings(results) = results {
/// assert_eq!(results.into_values_iter().next().unwrap().unwrap()[0], Some(ex.into())); /// assert_eq!(results.into_values_iter().next().unwrap().unwrap()[0], Some(ex.into()));
/// } /// }
/// ``` /// ```
fn prepare_query(&self, query: &str, options: QueryOptions) -> Result<Self::PreparedQuery>; fn prepare_query<'a>(&'a self, query: &str, base_iri: Option<&'a str>) -> Result<Self::PreparedQuery>;
/// This is similar to `prepare_query`, but useful if a SPARQL query has already been parsed, which is the case when building `ServiceHandler`s for federated queries with `SERVICE` clauses. For examples, look in the tests.
fn prepare_query_from_pattern<'a>(
&'a self,
graph_pattern: &'a GraphPattern,
base_iri: Option<&str>,
) -> Result<Self::PreparedQuery>;
/// Retrieves quads with a filter on each quad component /// Retrieves quads with a filter on each quad component
/// ///

File diff suppressed because it is too large Load Diff

@ -9,6 +9,7 @@ mod plan;
mod plan_builder; mod plan_builder;
mod xml_results; mod xml_results;
use crate::model::NamedNode;
use crate::sparql::algebra::QueryVariants; use crate::sparql::algebra::QueryVariants;
use crate::sparql::eval::SimpleEvaluator; use crate::sparql::eval::SimpleEvaluator;
use crate::sparql::parser::read_sparql_query; use crate::sparql::parser::read_sparql_query;
@ -17,8 +18,11 @@ use crate::sparql::plan::{DatasetView, PlanNode};
use crate::sparql::plan_builder::PlanBuilder; use crate::sparql::plan_builder::PlanBuilder;
use crate::store::StoreConnection; use crate::store::StoreConnection;
use crate::Result; use crate::Result;
use failure::format_err;
use rio_api::iri::Iri;
use std::fmt; use std::fmt;
pub use crate::sparql::algebra::GraphPattern;
pub use crate::sparql::model::BindingsIterator; pub use crate::sparql::model::BindingsIterator;
pub use crate::sparql::model::QueryResult; pub use crate::sparql::model::QueryResult;
pub use crate::sparql::model::QueryResultSyntax; pub use crate::sparql::model::QueryResultSyntax;
@ -27,7 +31,7 @@ pub use crate::sparql::model::Variable;
/// A prepared [SPARQL query](https://www.w3.org/TR/sparql11-query/) /// A prepared [SPARQL query](https://www.w3.org/TR/sparql11-query/)
pub trait PreparedQuery { pub trait PreparedQuery {
/// Evaluates the query and returns its results /// Evaluates the query and returns its results
fn exec(&self) -> Result<QueryResult>; fn exec<'a>(&'a self, options: &'a QueryOptions<'a>) -> Result<QueryResult<'a>>;
} }
/// An implementation of `PreparedQuery` for internal use /// An implementation of `PreparedQuery` for internal use
@ -54,11 +58,11 @@ enum SimplePreparedQueryAction<S: StoreConnection> {
}, },
} }
impl<S: StoreConnection> SimplePreparedQuery<S> { impl<'a, S: StoreConnection + 'a> SimplePreparedQuery<S> {
pub(crate) fn new(connection: S, query: &str, options: QueryOptions) -> Result<Self> { pub(crate) fn new(connection: S, query: &str, base_iri: Option<&'a str>) -> Result<Self> {
let dataset = DatasetView::new(connection, options.default_graph_as_union); let dataset = DatasetView::new(connection);
//TODO avoid inserting terms in the Repository StringStore //TODO avoid inserting terms in the Repository StringStore
Ok(Self(match read_sparql_query(query, options.base_iri)? { Ok(Self(match read_sparql_query(query, base_iri)? {
QueryVariants::Select { QueryVariants::Select {
algebra, algebra,
dataset: _, dataset: _,
@ -112,35 +116,67 @@ impl<S: StoreConnection> SimplePreparedQuery<S> {
} }
})) }))
} }
/// Builds SimplePreparedQuery from an existing `GraphPattern`. This is used to support federated queries via `SERVICE` clauses
pub(crate) fn new_from_pattern(
connection: S,
pattern: &GraphPattern,
base_iri: Option<&'a str>
) -> Result<Self> {
let dataset = DatasetView::new(connection);
let (plan, variables) = PlanBuilder::build(dataset.encoder(), pattern)?;
let base_iri = base_iri.map(|str_iri| Iri::parse(str_iri.to_string()));
match base_iri {
Some(Err(_)) => Err(format_err!("Failed to parse base_iri")),
Some(Ok(base_iri)) =>
Ok(Self(SimplePreparedQueryAction::Select {
plan,
variables,
evaluator: SimpleEvaluator::new(dataset, Some(base_iri)),
})),
None =>
Ok(Self(SimplePreparedQueryAction::Select {
plan,
variables,
evaluator: SimpleEvaluator::new(dataset, None),
}))
}
}
} }
impl<S: StoreConnection> PreparedQuery for SimplePreparedQuery<S> { impl<S: StoreConnection> PreparedQuery for SimplePreparedQuery<S> {
fn exec(&self) -> Result<QueryResult<'_>> { fn exec<'a>(&'a self, options: &'a QueryOptions<'a>) -> Result<QueryResult<'a>> {
match &self.0 { match &self.0 {
SimplePreparedQueryAction::Select { SimplePreparedQueryAction::Select {
plan, plan,
variables, variables,
evaluator, evaluator,
} => evaluator.evaluate_select_plan(&plan, &variables), } => evaluator.evaluate_select_plan(&plan, &variables, options),
SimplePreparedQueryAction::Ask { plan, evaluator } => { SimplePreparedQueryAction::Ask { plan, evaluator } => {
evaluator.evaluate_ask_plan(&plan) evaluator.evaluate_ask_plan(&plan, options)
} }
SimplePreparedQueryAction::Construct { SimplePreparedQueryAction::Construct {
plan, plan,
construct, construct,
evaluator, evaluator,
} => evaluator.evaluate_construct_plan(&plan, &construct), } => evaluator.evaluate_construct_plan(&plan, &construct, &options),
SimplePreparedQueryAction::Describe { plan, evaluator } => { SimplePreparedQueryAction::Describe { plan, evaluator } => {
evaluator.evaluate_describe_plan(&plan) evaluator.evaluate_describe_plan(&plan, &options)
} }
} }
} }
} }
pub trait ServiceHandler {
fn handle<'a>(&'a self, node: NamedNode) -> Option<(fn(GraphPattern) -> Result<BindingsIterator<'a>>)>;
}
/// Options for SPARQL query parsing and evaluation like the query base IRI /// Options for SPARQL query parsing and evaluation like the query base IRI
pub struct QueryOptions<'a> { pub struct QueryOptions<'a> {
pub(crate) base_iri: Option<&'a str>, pub(crate) base_iri: Option<&'a str>,
pub(crate) default_graph_as_union: bool, pub(crate) default_graph_as_union: bool,
pub(crate) service_handler: Option<Box<dyn ServiceHandler>>,
} }
impl<'a> Default for QueryOptions<'a> { impl<'a> Default for QueryOptions<'a> {
@ -148,6 +184,7 @@ impl<'a> Default for QueryOptions<'a> {
Self { Self {
base_iri: None, base_iri: None,
default_graph_as_union: false, default_graph_as_union: false,
service_handler: None as Option<Box<dyn ServiceHandler>>,
} }
} }
} }
@ -164,6 +201,12 @@ impl<'a> QueryOptions<'a> {
self.default_graph_as_union = true; self.default_graph_as_union = true;
self self
} }
/// Consider the union of all graphs in the repository as the default graph
pub fn with_service_handler(mut self, service_handler: Box<dyn ServiceHandler>) -> Self {
self.service_handler = Some(service_handler);
self
}
} }
/// A parsed [SPARQL query](https://www.w3.org/TR/sparql11-query/) /// A parsed [SPARQL query](https://www.w3.org/TR/sparql11-query/)

@ -116,7 +116,7 @@ pub struct BindingsIterator<'a> {
} }
impl<'a> BindingsIterator<'a> { impl<'a> BindingsIterator<'a> {
pub(crate) fn new( pub fn new(
variables: Vec<Variable>, variables: Vec<Variable>,
iter: Box<dyn Iterator<Item = Result<Vec<Option<Term>>>> + 'a>, iter: Box<dyn Iterator<Item = Result<Vec<Option<Term>>>> + 'a>,
) -> Self { ) -> Self {

@ -1,3 +1,5 @@
use crate::sparql::GraphPattern;
use crate::sparql::model::Variable;
use crate::sparql::eval::StringOrStoreString; use crate::sparql::eval::StringOrStoreString;
use crate::store::numeric_encoder::{ use crate::store::numeric_encoder::{
EncodedQuad, EncodedTerm, Encoder, MemoryStrStore, StrContainer, StrLookup, EncodedQuad, EncodedTerm, Encoder, MemoryStrStore, StrContainer, StrLookup,
@ -16,6 +18,13 @@ pub enum PlanNode {
StaticBindings { StaticBindings {
tuples: Vec<EncodedTuple>, tuples: Vec<EncodedTuple>,
}, },
Service {
service_name: PatternValue,
variables: Vec<Variable>,
child: Box<PlanNode>,
graph_pattern: GraphPattern,
silent: bool,
},
QuadPatternJoin { QuadPatternJoin {
child: Box<PlanNode>, child: Box<PlanNode>,
subject: PatternValue, subject: PatternValue,
@ -161,6 +170,7 @@ impl PlanNode {
set.insert(*position); set.insert(*position);
child.add_variables(set); child.add_variables(set);
} }
PlanNode::Service { child, .. } => child.add_variables(set),
PlanNode::Sort { child, .. } => child.add_variables(set), PlanNode::Sort { child, .. } => child.add_variables(set),
PlanNode::HashDeduplicate { child } => child.add_variables(set), PlanNode::HashDeduplicate { child } => child.add_variables(set),
PlanNode::Skip { child, .. } => child.add_variables(set), PlanNode::Skip { child, .. } => child.add_variables(set),
@ -463,15 +473,13 @@ pub enum TripleTemplateValue {
pub struct DatasetView<S: StoreConnection> { pub struct DatasetView<S: StoreConnection> {
store: S, store: S,
extra: RefCell<MemoryStrStore>, extra: RefCell<MemoryStrStore>,
default_graph_as_union: bool,
} }
impl<S: StoreConnection> DatasetView<S> { impl<S: StoreConnection> DatasetView<S> {
pub fn new(store: S, default_graph_as_union: bool) -> Self { pub fn new(store: S) -> Self {
Self { Self {
store, store,
extra: RefCell::new(MemoryStrStore::default()), extra: RefCell::new(MemoryStrStore::default()),
default_graph_as_union,
} }
} }
@ -481,6 +489,7 @@ impl<S: StoreConnection> DatasetView<S> {
predicate: Option<EncodedTerm>, predicate: Option<EncodedTerm>,
object: Option<EncodedTerm>, object: Option<EncodedTerm>,
graph_name: Option<EncodedTerm>, graph_name: Option<EncodedTerm>,
default_graph_as_union: bool,
) -> Box<dyn Iterator<Item = Result<EncodedQuad>> + 'a> { ) -> Box<dyn Iterator<Item = Result<EncodedQuad>> + 'a> {
if graph_name == None { if graph_name == None {
Box::new( Box::new(
@ -491,7 +500,7 @@ impl<S: StoreConnection> DatasetView<S> {
Ok(quad) => quad.graph_name != ENCODED_DEFAULT_GRAPH, Ok(quad) => quad.graph_name != ENCODED_DEFAULT_GRAPH,
}), }),
) )
} else if graph_name == Some(ENCODED_DEFAULT_GRAPH) && self.default_graph_as_union { } else if graph_name == Some(ENCODED_DEFAULT_GRAPH) && default_graph_as_union {
Box::new( Box::new(
self.store self.store
.quads_for_pattern(subject, predicate, object, None) .quads_for_pattern(subject, predicate, object, None)

@ -103,11 +103,17 @@ impl<E: Encoder> PlanBuilder<E> {
left: Box::new(self.build_for_graph_pattern(a, variables, graph_name)?), left: Box::new(self.build_for_graph_pattern(a, variables, graph_name)?),
right: Box::new(self.build_for_graph_pattern(b, variables, graph_name)?), right: Box::new(self.build_for_graph_pattern(b, variables, graph_name)?),
}, },
GraphPattern::Service(_n, _p, _s) => { GraphPattern::Service(n, p, s) => {
return Err(format_err!( let service_name = self.pattern_value_from_named_node_or_variable(n, variables)?;
"SPARQL SERVICE clauses are not implemented yet" let graph_pattern = *p.clone();
)) PlanNode::Service {
} service_name,
variables: variables.clone(),
child: Box::new(self.build_for_graph_pattern(p, variables, service_name)?),
graph_pattern,
silent: *s,
}
},
GraphPattern::AggregateJoin(GroupPattern(key, p), aggregates) => { GraphPattern::AggregateJoin(GroupPattern(key, p), aggregates) => {
let mut inner_variables = key.clone(); let mut inner_variables = key.clone();
let inner_graph_name = let inner_graph_name =

@ -29,8 +29,9 @@ use std::sync::{PoisonError, RwLock, RwLockReadGuard, RwLockWriteGuard};
/// assert_eq!(vec![quad], results.unwrap()); /// assert_eq!(vec![quad], results.unwrap());
/// ///
/// // SPARQL query /// // SPARQL query
/// let prepared_query = connection.prepare_query("SELECT ?s WHERE { ?s ?p ?o }", QueryOptions::default()).unwrap(); /// let prepared_query = connection.prepare_query("SELECT ?s WHERE { ?s ?p ?o }", None).unwrap();
/// let results = prepared_query.exec().unwrap(); /// let options = QueryOptions::default();
/// let results = prepared_query.exec(&options).unwrap();
/// if let QueryResult::Bindings(results) = results { /// if let QueryResult::Bindings(results) = results {
/// assert_eq!(results.into_values_iter().next().unwrap().unwrap()[0], Some(ex.into())); /// assert_eq!(results.into_values_iter().next().unwrap().unwrap()[0], Some(ex.into()));
/// } /// }

@ -5,12 +5,13 @@ pub(crate) mod numeric_encoder;
#[cfg(feature = "rocksdb")] #[cfg(feature = "rocksdb")]
mod rocksdb; mod rocksdb;
pub use crate::sparql::GraphPattern;
pub use crate::store::memory::MemoryRepository; pub use crate::store::memory::MemoryRepository;
#[cfg(feature = "rocksdb")] #[cfg(feature = "rocksdb")]
pub use crate::store::rocksdb::RocksDbRepository; pub use crate::store::rocksdb::RocksDbRepository;
use crate::model::*; use crate::model::*;
use crate::sparql::{QueryOptions, SimplePreparedQuery}; use crate::sparql::{SimplePreparedQuery};
use crate::store::numeric_encoder::*; use crate::store::numeric_encoder::*;
use crate::{DatasetSyntax, GraphSyntax, RepositoryConnection, Result}; use crate::{DatasetSyntax, GraphSyntax, RepositoryConnection, Result};
use rio_api::parser::{QuadsParser, TriplesParser}; use rio_api::parser::{QuadsParser, TriplesParser};
@ -71,8 +72,8 @@ impl<S: StoreConnection> From<S> for StoreRepositoryConnection<S> {
impl<S: StoreConnection> RepositoryConnection for StoreRepositoryConnection<S> { impl<S: StoreConnection> RepositoryConnection for StoreRepositoryConnection<S> {
type PreparedQuery = SimplePreparedQuery<S>; type PreparedQuery = SimplePreparedQuery<S>;
fn prepare_query(&self, query: &str, options: QueryOptions) -> Result<SimplePreparedQuery<S>> { fn prepare_query<'a>(&self, query: &str, base_iri: Option<&'a str>) -> Result<SimplePreparedQuery<S>> {
SimplePreparedQuery::new(self.inner.clone(), query, options) //TODO: avoid clone SimplePreparedQuery::new(self.inner.clone(), query, base_iri) //TODO: avoid clone
} }
fn quads_for_pattern<'a>( fn quads_for_pattern<'a>(
@ -96,6 +97,15 @@ impl<S: StoreConnection> RepositoryConnection for StoreRepositoryConnection<S> {
) )
} }
fn prepare_query_from_pattern<'a>(
&'a self,
pattern: &GraphPattern,
base_iri: Option<&'a str>
) -> Result<Self::PreparedQuery> {
SimplePreparedQuery::new_from_pattern(self.inner.clone(), pattern, base_iri) //TODO: avoid clone
}
fn load_graph( fn load_graph(
&mut self, &mut self,
reader: impl BufRead, reader: impl BufRead,

@ -40,8 +40,9 @@ use std::str;
/// assert_eq!(vec![quad], results.unwrap()); /// assert_eq!(vec![quad], results.unwrap());
/// ///
/// // SPARQL query /// // SPARQL query
/// let prepared_query = connection.prepare_query("SELECT ?s WHERE { ?s ?p ?o }", QueryOptions::default()).unwrap(); /// let options = QueryOptions::default();
/// let results = prepared_query.exec().unwrap(); /// let prepared_query = connection.prepare_query("SELECT ?s WHERE { ?s ?p ?o }", None).unwrap();
/// let results = prepared_query.exec(&options).unwrap();
/// if let QueryResult::Bindings(results) = results { /// if let QueryResult::Bindings(results) = results {
/// assert_eq!(results.into_values_iter().next().unwrap().unwrap()[0], Some(ex.into())); /// assert_eq!(results.into_values_iter().next().unwrap().unwrap()[0], Some(ex.into()));
/// } /// }

@ -0,0 +1,240 @@
use rudf::model::*;
use rudf::{GraphSyntax, Repository, RepositoryConnection, MemoryRepository, Result};
use rudf::sparql::{BindingsIterator, GraphPattern, PreparedQuery, QueryOptions, QueryResult, ServiceHandler};
use failure::format_err;
use std::io::BufRead;
#[test]
fn simple_service_test() {
struct TestServiceHandler;
impl ServiceHandler for TestServiceHandler {
fn handle<'a>(&'a self, _named_node: NamedNode) -> Option<(fn(GraphPattern) -> Result<BindingsIterator<'a>>)> {
fn pattern_handler<'a>(graph_pattern: GraphPattern) -> Result<BindingsIterator<'a>> {
let triples = b"<http://example.com/s> <http://example.com/p> <http://example.com/o> .".as_ref();
do_pattern(triples, graph_pattern, QueryOptions::default())
};
Some(pattern_handler)
}
}
let query = r#"
SELECT ?s ?p ?o
WHERE
{
SERVICE <http://service1.org>
{ ?s ?p ?o
}
}
"#.to_string();
let options = QueryOptions::default().with_service_handler(Box::new(TestServiceHandler));
let results = do_query(b"".as_ref(), query, options).unwrap();
let collected = results.into_values_iter().map(move |b| b.unwrap()).collect::<Vec<_>>();
let solution = vec![
vec![ Some(ex(String::from("s"))), Some(ex(String::from("p"))), Some(ex(String::from("o"))) ],
];
assert_eq!(collected, solution);
}
#[test]
fn two_service_test() {
#[derive(Clone,Copy)]
struct TwoServiceTest;
impl ServiceHandler for TwoServiceTest {
fn handle<'a>(&'a self, named_node: NamedNode) -> Option<(fn(GraphPattern) -> Result<BindingsIterator<'a>>)> {
let service1 = NamedNode::parse("http://service1.org").unwrap();
let service2 = NamedNode::parse("http://service2.org").unwrap();
if named_node == service1 { Some(TwoServiceTest::handle_service1) }
else if named_node == service2 { Some(TwoServiceTest::handle_service2) }
else { None }
}
}
impl TwoServiceTest {
fn handle_service1<'a>(graph_pattern: GraphPattern) -> Result<BindingsIterator<'a>> {
let triples = br#"
<http://example.com/bob> <http://xmlns.com/foaf/0.1/name> "Bob" .
<http://example.com/alice> <http://xmlns.com/foaf/0.1/name> "Alice" .
"#.as_ref();
do_pattern(triples, graph_pattern, QueryOptions::default())
}
fn handle_service2<'a>(graph_pattern: GraphPattern) -> Result<BindingsIterator<'a>> {
let triples = br#"
<http://example.com/bob> <http://xmlns.com/foaf/0.1/mbox> <mailto:bob@example.com> .
<http://example.com/alice> <http://xmlns.com/foaf/0.1/mbox> <mailto:alice@example.com> .
"#.as_ref();
do_pattern(triples, graph_pattern, QueryOptions::default())
}
}
let query = r#"
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
SELECT ?name ?mbox
WHERE
{
SERVICE <http://service1.org>
{ ?s foaf:name ?name
}
SERVICE <http://service2.org>
{ ?s foaf:mbox ?mbox
}
}
ORDER BY ?name
"#.to_string();
let options = QueryOptions::default().with_service_handler(Box::new(TwoServiceTest));
let results = do_query(b"".as_ref(), query, options).unwrap();
let collected = results.into_values_iter().map(move |b| b.unwrap()).collect::<Vec<_>>();
let solution = vec![
vec![ Some(literal("Alice".to_string())), Some(mailto("alice@example.com".to_string())) ],
vec![ Some(literal("Bob".to_string())), Some(mailto("bob@example.com".to_string())) ],
];
assert_eq!(collected, solution);
}
#[test]
fn silent_service_empty_set_test() {
#[derive(Clone,Copy)]
struct ServiceTest;
impl ServiceHandler for ServiceTest {
fn handle<'a>(&'a self, _named_node: NamedNode) -> Option<(fn(GraphPattern) -> Result<BindingsIterator<'a>>)> {
Some(ServiceTest::handle_service)
}
}
impl ServiceTest {
fn handle_service<'a>(_graph_pattern: GraphPattern) -> Result<BindingsIterator<'a>> {
Err(format_err!("This is supposed to fail"))
}
}
let query = r#"
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
SELECT ?name ?mbox
WHERE
{
SERVICE SILENT <http://service1.org>
{ ?s foaf:name ?name
}
}
ORDER BY ?name
"#.to_string();
let triples = b"".as_ref();
let options = QueryOptions::default().with_service_handler(Box::new(ServiceTest));
let results = do_query(triples, query, options).unwrap();
let collected = results.into_values_iter().map(move |b| b.unwrap()).collect::<Vec<_>>();
println!("Collected: {:?}", collected);
assert_eq!(collected.len(), 0);
}
#[test]
fn non_silent_service_test() {
#[derive(Clone,Copy)]
struct ServiceTest;
impl ServiceHandler for ServiceTest {
fn handle<'a>(&'a self, _named_node: NamedNode) -> Option<(fn(GraphPattern) -> Result<BindingsIterator<'a>>)> {
Some(ServiceTest::handle_service)
}
}
impl ServiceTest {
fn handle_service<'a>(_graph_pattern: GraphPattern) -> Result<BindingsIterator<'a>> {
Err(format_err!("This is supposed to fail"))
}
}
let query = r#"
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
SELECT ?name
WHERE
{
SERVICE <http://service1.org>
{ ?s foaf:name ?name
}
}
ORDER BY ?name
"#.to_string();
let triples = b"".as_ref();
let options = QueryOptions::default().with_service_handler(Box::new(ServiceTest));
let results = do_query(triples, query, options).unwrap();
let result = results.into_values_iter().next();
match result {
Some(Err(_)) => assert_eq!(true, true),
_ => assert_eq!(true, false, "This should have been an error since the service fails"),
}
}
fn ex(id: String) -> Term {
Term::NamedNode(NamedNode::parse(format!("http://example.com/{}", &id)).unwrap())
}
fn mailto(id: String) -> Term {
Term::NamedNode(NamedNode::parse(format!("mailto:{}", &id)).unwrap())
}
fn literal(str: String) -> Term {
Term::Literal(Literal::new_simple_literal(str))
}
fn make_repository(reader: impl BufRead) -> Result<MemoryRepository> {
let repository = MemoryRepository::default();
let mut connection = repository.connection()?;
connection.load_graph(reader, GraphSyntax::NTriples, None, None).unwrap();
Ok(repository)
}
fn query_repository<'a>(repository: MemoryRepository, query: String, options: QueryOptions<'a>) -> Result<BindingsIterator<'a>> {
let connection = repository.connection()?;
let prepared_query = connection.prepare_query(&query, None)?;
let result = prepared_query.exec(&options)?;
match result {
QueryResult::Bindings(iterator) => {
let (varaibles, iter) = iterator.destruct();
let collected = iter.collect::<Vec<_>>();
Ok(BindingsIterator::new(varaibles, Box::new(collected.into_iter())))
},
_ => Err(format_err!("Excpected bindings but got another QueryResult"))
}
}
fn pattern_repository<'a>(repository: MemoryRepository, pattern: GraphPattern, options: QueryOptions<'a>) -> Result<BindingsIterator<'a>> {
let connection = repository.connection()?;
let prepared_query = connection.prepare_query_from_pattern(&pattern, None)?;
let result = prepared_query.exec(&options)?;
match result {
QueryResult::Bindings(iterator) => {
let (varaibles, iter) = iterator.destruct();
let collected = iter.collect::<Vec<_>>();
Ok(BindingsIterator::new(varaibles, Box::new(collected.into_iter())))
}
_ => Err(format_err!("Excpected bindings but got another QueryResult"))
}
}
fn do_query<'a>(reader: impl BufRead, query: String, options: QueryOptions<'a>) -> Result<BindingsIterator<'a>> {
let repository = make_repository(reader)?;
query_repository(repository, query, options)
}
fn do_pattern<'a>(reader: impl BufRead, pattern: GraphPattern, options: QueryOptions<'a>) -> Result<BindingsIterator<'a>> {
let repository = make_repository(reader)?;
pattern_repository(repository, pattern, options)
}

@ -158,13 +158,13 @@ fn sparql_w3c_query_evaluation_testsuite() -> Result<()> {
} }
match repository match repository
.connection()? .connection()?
.prepare_query(&read_file_to_string(&test.query)?, QueryOptions::default().with_base_iri(&test.query)) .prepare_query(&read_file_to_string(&test.query)?, Some(&test.query))
{ {
Err(error) => Err(format_err!( Err(error) => Err(format_err!(
"Failure to parse query of {} with error: {}", "Failure to parse query of {} with error: {}",
test, error test, error
)), )),
Ok(query) => match query.exec() { Ok(query) => match query.exec(&QueryOptions::default()) {
Err(error) => Err(format_err!( Err(error) => Err(format_err!(
"Failure to execute query of {} with error: {}", "Failure to execute query of {} with error: {}",
test, error test, error

@ -148,9 +148,10 @@ fn evaluate_sparql_query<R: RepositoryConnection>(
request: &Request, request: &Request,
) -> Response { ) -> Response {
//TODO: stream //TODO: stream
match connection.prepare_query(query, QueryOptions::default()) { let options = QueryOptions::default();
match connection.prepare_query(query, None) {
Ok(query) => { Ok(query) => {
let results = query.exec().unwrap(); let results = query.exec(&options).unwrap();
if let QueryResult::Graph(_) = results { if let QueryResult::Graph(_) = results {
let supported_formats = [ let supported_formats = [
GraphSyntax::NTriples.media_type(), GraphSyntax::NTriples.media_type(),

Loading…
Cancel
Save