diff --git a/lib/src/sparql/algebra.rs b/lib/src/sparql/algebra.rs index 6ba5f20a..36a28392 100644 --- a/lib/src/sparql/algebra.rs +++ b/lib/src/sparql/algebra.rs @@ -3,7 +3,6 @@ use std::collections::BTreeMap; use std::collections::BTreeSet; use std::fmt; use std::ops::Add; -use store::MemoryGraph; use utils::Escaper; use uuid::Uuid; use Result; @@ -1577,5 +1576,5 @@ impl fmt::Display for Query { pub enum QueryResult<'a> { Bindings(BindingsIterator<'a>), Boolean(bool), - Graph(MemoryGraph), + Graph(Box> + 'a>), } diff --git a/lib/src/sparql/eval.rs b/lib/src/sparql/eval.rs index 4e04deaf..6ac8acc6 100644 --- a/lib/src/sparql/eval.rs +++ b/lib/src/sparql/eval.rs @@ -2,6 +2,7 @@ use chrono::DateTime; use chrono::NaiveDateTime; use language_tags::LanguageTag; use model::BlankNode; +use model::Triple; use num_traits::identities::Zero; use num_traits::FromPrimitive; use num_traits::One; @@ -59,6 +60,20 @@ impl SimpleEvaluator { } } + pub fn evaluate_construct_plan<'a>( + &'a self, + plan: &'a PlanNode, + construct: &'a [TripleTemplate], + ) -> Result> { + Ok(QueryResult::Graph(Box::new(ConstructIterator { + store: self.store.clone(), + iter: self.eval_plan(plan, vec![]), + template: construct, + buffered_results: Vec::default(), + bnodes: Vec::default(), + }))) + } + fn eval_plan<'a>(&self, node: &'a PlanNode, from: EncodedTuple) -> EncodedTuplesIterator<'a> { match node { PlanNode::Init => Box::new(once(Ok(from))), @@ -1017,3 +1032,74 @@ impl<'a, S: EncodedQuadsStore> Iterator for UnionIterator<'a, S> { self.next() } } + +struct ConstructIterator<'a, S: EncodedQuadsStore> { + store: Arc, + iter: EncodedTuplesIterator<'a>, + template: &'a [TripleTemplate], + buffered_results: Vec>, + bnodes: Vec, +} + +impl<'a, S: EncodedQuadsStore> Iterator for ConstructIterator<'a, S> { + type Item = Result; + + fn next(&mut self) -> Option> { + if let Some(result) = self.buffered_results.pop() { + return Some(result); + } + { + let tuple = match self.iter.next()? { + Ok(tuple) => tuple, + Err(error) => return Some(Err(error)), + }; + let encoder = self.store.encoder(); + for template in self.template { + if let (Some(subject), Some(predicate), Some(object)) = ( + get_triple_template_value(&template.subject, &tuple, &mut self.bnodes), + get_triple_template_value(&template.predicate, &tuple, &mut self.bnodes), + get_triple_template_value(&template.object, &tuple, &mut self.bnodes), + ) { + self.buffered_results + .push(decode_triple(&encoder, subject, predicate, object)); + } else { + self.buffered_results.clear(); //No match, we do not output any triple for this row + break; + } + } + self.bnodes.clear(); //We do not reuse old bnodes + } + self.next() + } +} + +fn get_triple_template_value( + selector: &TripleTemplateValue, + tuple: &[Option], + bnodes: &mut Vec, +) -> Option { + match selector { + TripleTemplateValue::Constant(term) => Some(*term), + TripleTemplateValue::Variable(v) => get_tuple_value(*v, tuple), + TripleTemplateValue::BlankNode(id) => { + //TODO use resize_with + while *id >= tuple.len() { + bnodes.push(BlankNode::default()) + } + tuple[*id] + } + } +} + +fn decode_triple( + encoder: &Encoder, + subject: EncodedTerm, + predicate: EncodedTerm, + object: EncodedTerm, +) -> Result { + Ok(Triple::new( + encoder.decode_named_or_blank_node(subject)?, + encoder.decode_named_node(predicate)?, + encoder.decode_term(object)?, + )) +} diff --git a/lib/src/sparql/mod.rs b/lib/src/sparql/mod.rs index ef87807e..d5af0f14 100644 --- a/lib/src/sparql/mod.rs +++ b/lib/src/sparql/mod.rs @@ -9,6 +9,7 @@ use sparql::eval::SimpleEvaluator; use sparql::parser::read_sparql_query; use sparql::plan::PlanBuilder; use sparql::plan::PlanNode; +use sparql::plan::TripleTemplate; use std::io::Read; use store::encoded::EncodedQuadsStore; use store::encoded::StoreDataset; @@ -51,6 +52,19 @@ impl SparqlDataset for StoreDataset { evaluator: SimpleEvaluator::new(store), } } + Query::Construct { + construct, + algebra, + dataset, + } => { + let store = self.encoded(); + let (plan, variables) = PlanBuilder::build(&*store, &algebra)?; + SimplePreparedQuery::Construct { + plan, + construct: PlanBuilder::build_graph_template(&*store, &construct, variables)?, + evaluator: SimpleEvaluator::new(store), + } + } _ => unimplemented!(), }) } @@ -66,6 +80,11 @@ pub enum SimplePreparedQuery { plan: PlanNode, evaluator: SimpleEvaluator, }, + Construct { + plan: PlanNode, + construct: Vec, + evaluator: SimpleEvaluator, + }, } impl PreparedQuery for SimplePreparedQuery { @@ -77,6 +96,11 @@ impl PreparedQuery for SimplePreparedQuery { evaluator, } => evaluator.evaluate_select_plan(&plan, &variables), SimplePreparedQuery::Ask { plan, evaluator } => evaluator.evaluate_ask_plan(&plan), + SimplePreparedQuery::Construct { + plan, + construct, + evaluator, + } => evaluator.evaluate_construct_plan(&plan, &construct), } } } diff --git a/lib/src/sparql/plan.rs b/lib/src/sparql/plan.rs index 273945de..a2bb3d4b 100644 --- a/lib/src/sparql/plan.rs +++ b/lib/src/sparql/plan.rs @@ -305,6 +305,20 @@ pub enum Comparator { Desc(PlanExpression), } +#[derive(Eq, PartialEq, Debug, Clone, Copy, Hash)] +pub struct TripleTemplate { + pub subject: TripleTemplateValue, + pub predicate: TripleTemplateValue, + pub object: TripleTemplateValue, +} + +#[derive(Eq, PartialEq, Debug, Clone, Copy, Hash)] +pub enum TripleTemplateValue { + Constant(EncodedTerm), + BlankNode(usize), + Variable(usize), +} + pub struct PlanBuilder<'a, S: EncodedQuadsStore> { store: &'a S, } @@ -321,6 +335,14 @@ impl<'a, S: EncodedQuadsStore> PlanBuilder<'a, S> { Ok((plan, variables)) } + pub fn build_graph_template( + store: &S, + template: &[TriplePattern], + mut variables: Vec, + ) -> Result> { + PlanBuilder { store }.build_for_graph_template(template, &mut variables) + } + fn build_for_graph_pattern( &self, pattern: &GraphPattern, @@ -704,6 +726,71 @@ impl<'a, S: EncodedQuadsStore> PlanBuilder<'a, S> { Ok(result) }).collect() } + + fn build_for_graph_template( + &self, + template: &[TriplePattern], + variables: &mut Vec, + ) -> Result> { + let mut bnodes = Vec::default(); + template + .into_iter() + .map(|triple| { + Ok(TripleTemplate { + subject: self.template_value_from_term_or_variable( + &triple.subject, + variables, + &mut bnodes, + )?, + predicate: self.template_value_from_named_node_or_variable( + &triple.predicate, + variables, + &mut bnodes, + )?, + object: self.template_value_from_term_or_variable( + &triple.object, + variables, + &mut bnodes, + )?, + }) + }).collect() + } + + fn template_value_from_term_or_variable( + &self, + term_or_variable: &TermOrVariable, + variables: &mut Vec, + bnodes: &mut Vec, + ) -> Result { + Ok(match term_or_variable { + TermOrVariable::Term(term) => { + TripleTemplateValue::Constant(self.store.encoder().encode_term(term)?) + } + TermOrVariable::Variable(variable) => if variable.has_name() { + TripleTemplateValue::Variable(variable_key(variables, variable)) + } else { + TripleTemplateValue::BlankNode(variable_key(bnodes, variable)) + }, + }) + } + + fn template_value_from_named_node_or_variable( + &self, + named_node_or_variable: &NamedNodeOrVariable, + variables: &mut Vec, + bnodes: &mut Vec, + ) -> Result { + Ok(match named_node_or_variable { + NamedNodeOrVariable::NamedNode(term) => { + TripleTemplateValue::Constant(self.store.encoder().encode_named_node(term)?) + } + NamedNodeOrVariable::Variable(variable) => if variable.has_name() { + TripleTemplateValue::Variable(variable_key(variables, variable)) + } else { + TripleTemplateValue::BlankNode(variable_key(bnodes, variable)) + }, + }) + } } fn variable_key(variables: &mut Vec, variable: &Variable) -> usize { diff --git a/lib/tests/sparql_test_cases.rs b/lib/tests/sparql_test_cases.rs index 2626d6f2..a2f3c8dd 100644 --- a/lib/tests/sparql_test_cases.rs +++ b/lib/tests/sparql_test_cases.rs @@ -99,6 +99,8 @@ fn sparql_w3c_query_evaluation_testsuite() { "http://www.w3.org/2001/sw/DataAccess/tests/data-r2/bound/manifest.ttl", ).unwrap(), Url::parse("http://www.w3.org/2001/sw/DataAccess/tests/data-r2/cast/manifest.ttl").unwrap(), + Url::parse("http://www.w3.org/2001/sw/DataAccess/tests/data-r2/construct/manifest.ttl") + .unwrap(), Url::parse("http://www.w3.org/2001/sw/DataAccess/tests/data-r2/distinct/manifest.ttl") .unwrap(), Url::parse("http://www.w3.org/2001/sw/DataAccess/tests/data-r2/expr-builtin/manifest.ttl") @@ -341,7 +343,7 @@ mod rs { fn to_graph(result: QueryResult, with_order: bool) -> Result { match result { - QueryResult::Graph(graph) => Ok(graph), + QueryResult::Graph(graph) => graph.collect(), QueryResult::Boolean(value) => { let graph = MemoryGraph::default(); let result_set = BlankNode::default();