diff --git a/lib/src/sparql/eval.rs b/lib/src/sparql/eval.rs index 5b9e9a93..bc2410bc 100644 --- a/lib/src/sparql/eval.rs +++ b/lib/src/sparql/eval.rs @@ -131,11 +131,56 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { PlanNode::Init => Box::new(once(Ok(from))), PlanNode::StaticBindings { tuples } => Box::new(tuples.iter().cloned().map(Ok)), PlanNode::Service { - child, + variables, + silent, + service_name, + graph_pattern, .. } => { - println!("Service!"); - self.eval_plan(&*child, from, options) + match &options.service_handler { + None => if *silent { + return Box::new(vec![].into_iter()); + } else { + return Box::new(once(Err(format_err!( + "No handler was supplied to resolve the given service" + )))) as EncodedTuplesIterator<'_>; + }, + Some(handler) => { + let pattern_option = match get_pattern_value(service_name, &[]) { + None => if *silent { + return Box::new(vec![].into_iter()); + } else { + return Box::new(once(Err(format_err!( + "The handler supplied was unable to evaluate the given service" + )))) as EncodedTuplesIterator<'_>; + }, + Some(term) => { + let named_node = self.dataset.decode_named_node(term).unwrap(); + handler.handle(named_node) + }, + }; + + match pattern_option { + None => if *silent { + return Box::new(vec![].into_iter()); + } else { + return Box::new(once(Err(format_err!( + "The handler supplied was unable to produce any result set on the given service" + )))) as EncodedTuplesIterator<'_>; + }, + Some(pattern_fn) => { + let bindings = pattern_fn(graph_pattern.clone()).unwrap(); + let encoded = self.encode_bindings(variables, bindings); + let collected = encoded.collect::>(); + Box::new(JoinIterator { + left: vec![from], + right_iter: Box::new(collected.into_iter()), + buffered_results: vec![], + }) + }, + } + } + } }, PlanNode::QuadPatternJoin { child, @@ -1589,6 +1634,45 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { ) } + fn encode_bindings<'b>( + &'b self, + variables: &'b [Variable], + iter: BindingsIterator<'b>, + ) -> EncodedTuplesIterator<'b> + where + 'a: 'b, + { + let mut encoder = self.dataset.encoder(); + let (binding_variables, iter) = BindingsIterator::destruct(iter); + let mut combined_variables = variables.clone().to_vec(); + for v in binding_variables.clone() { + if !combined_variables.contains(&v) { + combined_variables.resize(combined_variables.len() + 1, v); + } + } + + println!("binding_variables: {:?}", binding_variables.clone()); + println!("variables: {:?}", variables.clone()); + println!("combined_variables: {:?}", combined_variables.clone()); + println!("\n\n"); + Box::new(iter.map(move |terms| { + let mut encoded_terms = vec![None; combined_variables.len()]; + for (i, term_option) in terms?.into_iter().enumerate() { + match term_option { + None => (), + Some(term) => { + if let Ok(encoded) = encoder.encode_term(&term) { + let variable = binding_variables[i].clone(); + put_variable_value(&variable, &combined_variables, encoded, &mut encoded_terms) + } + } + } + } + Ok(encoded_terms) + })) + } + + #[allow(clippy::float_cmp)] fn equals(&self, a: EncodedTerm, b: EncodedTerm) -> Option { match a { @@ -1963,6 +2047,16 @@ fn put_pattern_value(selector: &PatternValue, value: EncodedTerm, tuple: &mut En } } +fn put_variable_value(selector: &Variable, variables: &[Variable], value: EncodedTerm, tuple: &mut EncodedTuple) { + for (i, v) in variables.iter().enumerate() { + if selector == v { + put_value(i, value, tuple); + break; + } + } +} + + fn put_value(position: usize, value: EncodedTerm, tuple: &mut EncodedTuple) { if position < tuple.len() { tuple[position] = Some(value) diff --git a/lib/src/sparql/mod.rs b/lib/src/sparql/mod.rs index 8a11e94b..491acb99 100644 --- a/lib/src/sparql/mod.rs +++ b/lib/src/sparql/mod.rs @@ -188,6 +188,12 @@ impl<'a> QueryOptions<'a> { self.default_graph_as_union = true; self } + + /// Consider the union of all graphs in the repository as the default graph + pub fn with_service_handler(mut self, service_handler: Box) -> Self { + self.service_handler = Some(service_handler); + self + } } /// A parsed [SPARQL query](https://www.w3.org/TR/sparql11-query/) diff --git a/lib/tests/service_test_cases.rs b/lib/tests/service_test_cases.rs index cdc5ef9a..342342f4 100644 --- a/lib/tests/service_test_cases.rs +++ b/lib/tests/service_test_cases.rs @@ -94,7 +94,7 @@ fn simple_service_test() { "#; - let query_options = QueryOptions::default(); + let query_options = QueryOptions::default().with_service_handler(Box::new(TestServiceHandler)); let prepared_query = connection.prepare_query(query, &query_options).unwrap(); let results = prepared_query.exec(&query_options).unwrap(); if let QueryResult::Bindings(results) = results { @@ -107,73 +107,78 @@ fn simple_service_test() { 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>)> { - 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> { - let repository = MemoryRepository::default(); - let mut connection = repository.connection().unwrap(); - let file = br#" - "Bob" . - "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::>().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() { + #[derive(Clone,Copy)] + struct TwoServiceTest; + impl ServiceHandler for TwoServiceTest { + fn handle<'a>(&'a self, named_node: NamedNode) -> Option<(fn(GraphPattern) -> Result>)> { + 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} + } + } - fn handle_service2<'a>(graph_pattern: GraphPattern) -> Result> { - let repository = MemoryRepository::default(); - let mut connection = repository.connection().unwrap(); - let file = br#" - . - . - "#; - 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::>().into_iter(); - let new_iter = BindingsIterator::new(variables, Box::new(cloned_iter)); - Ok(new_iter) - }, - _ => Err(format_err!("Excpected bindings but got another QueryResult")) + impl TwoServiceTest { + + fn handle_service1<'a>(graph_pattern: GraphPattern) -> Result> { + let repository = MemoryRepository::default(); + let mut connection = repository.connection().unwrap(); + let file = br#" + "Bob" . + "Alice" . + "#; + connection.load_graph(file.as_ref(), GraphSyntax::NTriples, None, None).unwrap(); + let query_options = QueryOptions::default().with_service_handler(Box::new(TwoServiceTest)); + 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::>().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() { + + fn handle_service2<'a>(graph_pattern: GraphPattern) -> Result> { + let repository = MemoryRepository::default(); + let mut connection = repository.connection().unwrap(); + let file = br#" + . + . + "#; + connection.load_graph(file.as_ref(), GraphSyntax::NTriples, None, None).unwrap(); + let query_options = QueryOptions::default().with_service_handler(Box::new(TwoServiceTest)); + 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::>().into_iter(); + let new_iter = BindingsIterator::new(variables, Box::new(cloned_iter)); + Ok(new_iter) + }, + _ => Err(format_err!("Excpected bindings but got another QueryResult")) + } + } + + } + let repository = MemoryRepository::default(); let connection = repository.connection().unwrap(); @@ -193,9 +198,9 @@ fn two_service_test() { 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(); + let query_options = QueryOptions::default().with_service_handler(Box::new(TwoServiceTest)); + 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::>(); for c in collected.clone() { @@ -212,6 +217,5 @@ fn two_service_test() { assert_eq!(true, false); } } -*/