From 6532888da5bba4a77fa69669ebcf138ef2d252ea Mon Sep 17 00:00:00 2001 From: Dustin Whitney Date: Thu, 17 Oct 2019 12:54:46 -0400 Subject: [PATCH] custom functions are working --- lib/src/sparql/eval.rs | 13 ++++----- lib/src/sparql/mod.rs | 37 +++++++++++++++++++----- lib/tests/custom_functions_test_cases.rs | 25 ++++++++++++++-- 3 files changed, 59 insertions(+), 16 deletions(-) diff --git a/lib/src/sparql/eval.rs b/lib/src/sparql/eval.rs index 6777f57e..608cd901 100644 --- a/lib/src/sparql/eval.rs +++ b/lib/src/sparql/eval.rs @@ -3,7 +3,7 @@ use crate::model::Triple; use crate::sparql::algebra::GraphPattern; use crate::sparql::model::*; use crate::sparql::plan::*; -use crate::sparql::ServiceHandler; +use crate::sparql::{CustomFunctionsHandler, ServiceHandler}; use crate::store::numeric_encoder::*; use crate::store::StoreConnection; use crate::Result; @@ -45,6 +45,7 @@ pub struct SimpleEvaluator { bnodes_map: Mutex>, now: DateTime, service_handler: Box, + custom_functions_handler: Box, } impl<'a, S: StoreConnection + 'a> SimpleEvaluator { @@ -52,6 +53,7 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { dataset: DatasetView, base_iri: Option>, service_handler: Box, + custom_functions_handler: Box, ) -> Self { Self { dataset, @@ -59,6 +61,7 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { base_iri, now: Utc::now().with_timezone(&FixedOffset::east(0)), service_handler, + custom_functions_handler, } } @@ -1412,9 +1415,6 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { value_id: self.to_string_id(self.eval_expression(e, tuple)?)?, }), PlanExpression::CustomFunction { name, parameters } => { - println!("name: {:?}", name); - println!("parameters: {:?}", parameters); - println!("tuple: {:?}", tuple); let parameters = parameters .iter() .map(|p| { @@ -1422,9 +1422,8 @@ impl<'a, S: StoreConnection + 'a> SimpleEvaluator { .and_then(|encoded| self.dataset.decode_term(encoded).ok()) }) .collect::>(); - - println!("parameters: {:?}", parameters); - None + self.custom_functions_handler.handle(name, ¶meters) + .and_then(|term| self.dataset.encoder().encode_term(&term).ok()) } } } diff --git a/lib/src/sparql/mod.rs b/lib/src/sparql/mod.rs index f0c62666..6420e944 100644 --- a/lib/src/sparql/mod.rs +++ b/lib/src/sparql/mod.rs @@ -9,7 +9,7 @@ mod plan; mod plan_builder; mod xml_results; -use crate::model::NamedNode; +use crate::model::{NamedNode,Term}; use crate::sparql::algebra::QueryVariants; use crate::sparql::eval::SimpleEvaluator; use crate::sparql::parser::read_sparql_query; @@ -70,7 +70,7 @@ impl<'a, S: StoreConnection + 'a> SimplePreparedQuery { SimplePreparedQueryAction::Select { plan, variables, - evaluator: SimpleEvaluator::new(dataset, base_iri, options.service_handler), + evaluator: SimpleEvaluator::new(dataset, base_iri, options.service_handler, options.custom_functions_handler), } } QueryVariants::Ask { @@ -81,7 +81,7 @@ impl<'a, S: StoreConnection + 'a> SimplePreparedQuery { let (plan, _) = PlanBuilder::build(dataset.encoder(), &algebra)?; SimplePreparedQueryAction::Ask { plan, - evaluator: SimpleEvaluator::new(dataset, base_iri, options.service_handler), + evaluator: SimpleEvaluator::new(dataset, base_iri, options.service_handler, options.custom_functions_handler), } } QueryVariants::Construct { @@ -98,7 +98,7 @@ impl<'a, S: StoreConnection + 'a> SimplePreparedQuery { &construct, variables, )?, - evaluator: SimpleEvaluator::new(dataset, base_iri, options.service_handler), + evaluator: SimpleEvaluator::new(dataset, base_iri, options.service_handler, options.custom_functions_handler), } } QueryVariants::Describe { @@ -109,7 +109,7 @@ impl<'a, S: StoreConnection + 'a> SimplePreparedQuery { let (plan, _) = PlanBuilder::build(dataset.encoder(), &algebra)?; SimplePreparedQueryAction::Describe { plan, - evaluator: SimpleEvaluator::new(dataset, base_iri, options.service_handler), + evaluator: SimpleEvaluator::new(dataset, base_iri, options.service_handler, options.custom_functions_handler), } } })) @@ -131,7 +131,7 @@ impl<'a, S: StoreConnection + 'a> SimplePreparedQuery { Ok(Self(SimplePreparedQueryAction::Select { plan, variables, - evaluator: SimpleEvaluator::new(dataset, base_iri, options.service_handler), + evaluator: SimpleEvaluator::new(dataset, base_iri, options.service_handler, options.custom_functions_handler), })) } } @@ -185,11 +185,25 @@ impl ServiceHandler for EmptyServiceHandler { } } +pub trait CustomFunctionsHandler { + fn handle(&self, node: &NamedNode, parameters: &Vec>) -> Option; +} + +#[derive(Default)] +struct EmptyCustomFunctionsHandler {} + +impl CustomFunctionsHandler for EmptyCustomFunctionsHandler { + fn handle(&self, _node: &NamedNode, _parameters: &Vec>) -> Option { + None + } +} + /// Options for SPARQL query parsing and evaluation like the query base IRI pub struct QueryOptions<'a> { pub(crate) base_iri: Option<&'a str>, pub(crate) default_graph_as_union: bool, pub(crate) service_handler: Box, + pub(crate) custom_functions_handler: Box, } impl<'a> Default for QueryOptions<'a> { @@ -198,6 +212,7 @@ impl<'a> Default for QueryOptions<'a> { base_iri: None, default_graph_as_union: false, service_handler: Box::new(EmptyServiceHandler::default()), + custom_functions_handler: Box::new(EmptyCustomFunctionsHandler::default()), } } } @@ -215,11 +230,19 @@ impl<'a> QueryOptions<'a> { self } - /// Consider the union of all graphs in the repository as the default graph + /// Set a ServiceHandler to handle SERVICE clauses pub fn with_service_handler(mut self, service_handler: Box) -> Self { self.service_handler = service_handler; self } + + /// Set a CustomFunctionHandler to add your own functions + pub fn with_custom_functions_handler(mut self, custom_functions_handler: Box) -> Self { + self.custom_functions_handler = custom_functions_handler; + self + } + + } /// A parsed [SPARQL query](https://www.w3.org/TR/sparql11-query/) diff --git a/lib/tests/custom_functions_test_cases.rs b/lib/tests/custom_functions_test_cases.rs index 5e63e70d..c2fe2022 100644 --- a/lib/tests/custom_functions_test_cases.rs +++ b/lib/tests/custom_functions_test_cases.rs @@ -1,6 +1,6 @@ use rudf::model::*; use rudf::{GraphSyntax, Repository, RepositoryConnection, MemoryRepository, Result}; -use rudf::sparql::{BindingsIterator, PreparedQuery, QueryOptions, QueryResult}; +use rudf::sparql::{BindingsIterator, CustomFunctionsHandler, PreparedQuery, QueryOptions, QueryResult}; use failure::format_err; use std::io::BufRead; @@ -9,6 +9,27 @@ use std::io::BufRead; #[test] fn simple_custom_function_test() { + struct TestHandler; + + impl CustomFunctionsHandler for TestHandler { + fn handle(&self, node: &NamedNode, parameters: &Vec>) -> Option { + let reverse = NamedNode::parse("http://example.com#REVERSE").ok()?; + if *node == reverse { + let param = ¶meters[0]; + if let Some(Term::Literal(literal)) = param { + let value = literal.value(); + let reversed = value.chars().rev().collect::(); + let literal = Literal::new_simple_literal(reversed); + Some(Term::Literal(literal)) + } else { + None + } + } else { + None + } + } + } + let query = r#" PREFIX ex: SELECT ?name ?reverse @@ -21,7 +42,7 @@ fn simple_custom_function_test() { "#.to_string(); - let options = QueryOptions::default(); + let options = QueryOptions::default().with_custom_functions_handler(Box::new(TestHandler)); let triples = br#" "Bob" . "Alice" .