wiring hooked up

pull/11/head
Dustin Whitney 5 years ago
parent 40680956ba
commit aea8dcebb9
  1. 11
      lib/src/repository.rs
  2. 7
      lib/src/sparql/eval.rs
  3. 20
      lib/src/sparql/mod.rs
  4. 2
      lib/src/sparql/model.rs
  5. 8
      lib/src/sparql/plan.rs
  6. 13
      lib/src/sparql/plan_builder.rs
  7. 14
      lib/src/store/mod.rs
  8. 217
      lib/tests/service_test_cases.rs
  9. 4
      lib/tests/sparql_test_cases.rs

@ -1,5 +1,5 @@
use crate::model::*;
use crate::sparql::{PreparedQuery, QueryOptions};
use crate::sparql::{GraphPattern, PreparedQuery, QueryOptions};
use crate::{DatasetSyntax, GraphSyntax, Result};
use std::io::BufRead;
@ -81,7 +81,7 @@ pub trait RepositoryConnection: Clone {
/// 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, options: &'a QueryOptions<'a>) -> Result<Self::PreparedQuery>;
/// Retrieves quads with a filter on each quad component
///
@ -112,6 +112,13 @@ pub trait RepositoryConnection: Clone {
where
Self: 'a;
fn prepare_query_from_pattern<'a>(
&'a self,
graph_pattern: &'a GraphPattern,
options: &'a QueryOptions<'a>
) -> Result<Self::PreparedQuery>;
/// Loads a graph file (i.e. triples) into the repository
///
/// Usage example:

@ -130,6 +130,13 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator<S> {
match node {
PlanNode::Init => Box::new(once(Ok(from))),
PlanNode::StaticBindings { tuples } => Box::new(tuples.iter().cloned().map(Ok)),
PlanNode::Service {
child,
..
} => {
println!("Service!");
self.eval_plan(&*child, from, options)
},
PlanNode::QuadPatternJoin {
child,
subject,

@ -19,6 +19,7 @@ use crate::sparql::plan_builder::PlanBuilder;
use crate::store::StoreConnection;
use crate::Result;
use std::fmt;
use rio_api::iri::Iri;
pub use crate::sparql::algebra::GraphPattern;
pub use crate::sparql::model::BindingsIterator;
@ -56,8 +57,8 @@ enum SimplePreparedQueryAction<S: StoreConnection> {
},
}
impl<S: StoreConnection> SimplePreparedQuery<S> {
pub(crate) fn new(connection: S, query: &str, options: QueryOptions) -> Result<Self> {
impl<'a, S: StoreConnection + 'a> SimplePreparedQuery<S> {
pub(crate) fn new(connection: S, query: &str, options: &'a QueryOptions<'a>) -> Result<Self> {
let dataset = DatasetView::new(connection, options.default_graph_as_union);
//TODO avoid inserting terms in the Repository StringStore
Ok(Self(match read_sparql_query(query, options.base_iri)? {
@ -114,6 +115,21 @@ impl<S: StoreConnection> SimplePreparedQuery<S> {
}
}))
}
pub(crate) fn new_from_pattern(
connection: S,
pattern: &GraphPattern,
options: &'a QueryOptions<'a>
) -> Result<Self> {
let dataset = DatasetView::new(connection, options.default_graph_as_union);
let iri = options.base_iri.map(|i| Iri::parse(i.to_string()).unwrap());
let (plan, variables) = PlanBuilder::build(dataset.encoder(), pattern)?;
Ok(Self(SimplePreparedQueryAction::Select {
plan,
variables,
evaluator: SimpleEvaluator::new(dataset, iri),
}))
}
}
impl<S: StoreConnection> PreparedQuery for SimplePreparedQuery<S> {

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

@ -1,3 +1,4 @@
use crate::sparql::GraphPattern;
use crate::sparql::eval::StringOrStoreString;
use crate::store::numeric_encoder::{
EncodedQuad, EncodedTerm, Encoder, MemoryStrStore, StrContainer, StrLookup,
@ -16,6 +17,12 @@ pub enum PlanNode {
StaticBindings {
tuples: Vec<EncodedTuple>,
},
Service {
service_name: PatternValue,
child: Box<PlanNode>,
graph_pattern: GraphPattern,
silent: bool,
},
QuadPatternJoin {
child: Box<PlanNode>,
subject: PatternValue,
@ -161,6 +168,7 @@ impl PlanNode {
set.insert(*position);
child.add_variables(set);
}
PlanNode::Service { child, .. } => child.add_variables(set),
PlanNode::Sort { child, .. } => child.add_variables(set),
PlanNode::HashDeduplicate { child } => child.add_variables(set),
PlanNode::Skip { child, .. } => child.add_variables(set),

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

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

@ -0,0 +1,217 @@
use rudf::model::*;
use rudf::{GraphSyntax, Repository, RepositoryConnection, MemoryRepository, Result};
use rudf::sparql::{BindingsIterator, GraphPattern, PreparedQuery, QueryOptions, QueryResult, ServiceHandler};
use failure::format_err;
fn ex(id: String) -> Term {
Term::NamedNode(NamedNode::parse(format!("http://example.com/{}", &id)).unwrap())
}
fn foaf(id: String) -> Term {
Term::NamedNode(NamedNode::parse(format!("http://xmlns.com/foaf/0.1/{}", &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))
}
/*
#[derive(Clone,Copy)]
struct SimpleServiceTest;
impl ServiceHandler for SimpleServiceTest {
fn handle<'a>(&'a self, named_node: NamedNode) -> Option<(fn(GraphPattern) -> Result<BindingsIterator<'a>>)> {
Some(SimpleServiceTest::handle_service)
}
}
impl SimpleServiceTest {
fn handle_service<'a>(graph_pattern: GraphPattern) -> Result<BindingsIterator<'a>> {
let repository = MemoryRepository::default();
let mut connection = repository.connection().unwrap();
let file = b"<http://example.com/s> <http://example.com/p> <http://example.com/o> .";
connection.load_graph(file.as_ref(), GraphSyntax::NTriples, None, None).unwrap();
let prepared_query = connection.prepare_query_from_pattern(&graph_pattern, None).unwrap();
let result = prepared_query.exec(&Some(SimpleServiceTest)).unwrap();
match result {
QueryResult::Bindings(iterator) => {
let (variables, iter) = iterator.destruct();
let cloned_iter = iter.collect::<Vec<_>>().into_iter();
let new_iter = BindingsIterator::new(variables, Box::new(cloned_iter));
Ok(new_iter)
},
_ => Err(format_err!("Excpected bindings but got another QueryResult"))
}
}
}
*/
#[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 repository = MemoryRepository::default();
let mut connection = repository.connection().unwrap();
let file = b"<http://example.com/s> <http://example.com/p> <http://example.com/o> .";
connection.load_graph(file.as_ref(), GraphSyntax::NTriples, None, None).unwrap();
let query_options = QueryOptions::default();
let prepared_query = connection.prepare_query_from_pattern(&graph_pattern, &query_options).unwrap();
let result = prepared_query.exec(&query_options).unwrap();
match result {
QueryResult::Bindings(iterator) => {
let (variables, iter) = iterator.destruct();
let cloned_iter = iter.collect::<Vec<_>>().into_iter();
let new_iter = BindingsIterator::new(variables, Box::new(cloned_iter));
Ok(new_iter)
},
_ => Err(format_err!("Excpected bindings but got another QueryResult"))
}
};
Some(pattern_handler)
}
}
let repository = MemoryRepository::default();
let connection = repository.connection().unwrap();
let query = r#"
SELECT ?s ?p ?o
WHERE
{
SERVICE <http://service1.org>
{ ?s ?p ?o
}
}
"#;
let query_options = QueryOptions::default();
let prepared_query = connection.prepare_query(query, &query_options).unwrap();
let results = prepared_query.exec(&query_options).unwrap();
if let QueryResult::Bindings(results) = results {
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);
} else {
assert_eq!(true, false);
}
}
/*
#[derive(Clone,Copy)]
struct TwoServiceTest;
impl ServiceHandler for TwoServiceTest {
fn handle<'a>(&'a self, named_node: NamedNode) -> Option<(fn(GraphPattern) -> Result<BindingsIterator<'a>>)> {
println!("Handler called for {:?}", named_node);
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 repository = MemoryRepository::default();
let mut connection = repository.connection().unwrap();
let file = 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" .
"#;
connection.load_graph(file.as_ref(), GraphSyntax::NTriples, None, None).unwrap();
let prepared_query = connection.prepare_query_from_pattern(&graph_pattern, None).unwrap();
let result = prepared_query.exec(&Some(NoneService)).unwrap();
match result {
QueryResult::Bindings(iterator) => {
let (variables, iter) = iterator.destruct();
let cloned_iter = iter.collect::<Vec<_>>().into_iter();
let new_iter = BindingsIterator::new(variables, Box::new(cloned_iter));
Ok(new_iter)
},
_ => Err(format_err!("Excpected bindings but got another QueryResult"))
}
}
fn handle_service2<'a>(graph_pattern: GraphPattern) -> Result<BindingsIterator<'a>> {
let repository = MemoryRepository::default();
let mut connection = repository.connection().unwrap();
let file = 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> .
"#;
connection.load_graph(file.as_ref(), GraphSyntax::NTriples, None, None).unwrap();
let prepared_query = connection.prepare_query_from_pattern(&graph_pattern, None).unwrap();
let result = prepared_query.exec(&Some(NoneService)).unwrap();
match result {
QueryResult::Bindings(iterator) => {
let (variables, iter) = iterator.destruct();
let cloned_iter = iter.collect::<Vec<_>>().into_iter();
let new_iter = BindingsIterator::new(variables, Box::new(cloned_iter));
Ok(new_iter)
},
_ => Err(format_err!("Excpected bindings but got another QueryResult"))
}
}
}
#[test]
fn two_service_test() {
let repository = MemoryRepository::default();
let connection = repository.connection().unwrap();
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
"#;
let prepared_query = connection.prepare_query(query, None).unwrap();
let service_handler = Some(TwoServiceTest);
let results = prepared_query.exec(&service_handler).unwrap();
if let QueryResult::Bindings(results) = results {
let collected = results.into_values_iter().map(move |b| b.unwrap()).collect::<Vec<_>>();
for c in collected.clone() {
println!("{:?}", c);
}
println!("\n\n\n");
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())) ],
];
println!("Results: {:?}", collected);
assert_eq!(collected, solution);
} else {
assert_eq!(true, false);
}
}
*/

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

Loading…
Cancel
Save