From 258ac21500d133cb26460354e5f0b0063e6b1648 Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Champin Date: Fri, 22 Nov 2024 16:19:10 +0100 Subject: [PATCH] implement SPARQL Union --- sparql/src/exec.rs | 27 ++++++++++++++++++++++++++- sparql/src/test.rs | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 1 deletion(-) diff --git a/sparql/src/exec.rs b/sparql/src/exec.rs index e6fc22c..1f5314c 100644 --- a/sparql/src/exec.rs +++ b/sparql/src/exec.rs @@ -101,7 +101,7 @@ impl<'a, D: Dataset + ?Sized> ExecState<'a, D> { expression, } => Err(SparqlWrapperError::NotImplemented("LeftJoin")), Filter { expr, inner } => self.filter(expr, inner, graph_matcher, binding), - Union { left, right } => Err(SparqlWrapperError::NotImplemented("Union")), + Union { left, right } => self.union(left, right, graph_matcher, binding), Graph { name, inner } => Err(SparqlWrapperError::NotImplemented("Graph")), Extend { inner, @@ -184,6 +184,31 @@ impl<'a, D: Dataset + ?Sized> ExecState<'a, D> { Ok(Bindings { variables, iter }) } + fn union( + &mut self, + left: &GraphPattern, + right: &GraphPattern, + graph_matcher: &[Option], + binding: Option<&Binding>, + ) -> Result, SparqlWrapperError> { + let Bindings { + variables: lv, + iter: li, + } = self.select(left, graph_matcher, binding)?; + let Bindings { + variables: rv, + iter: ri, + } = self.select(right, graph_matcher, binding)?; + let mut variables = lv.clone(); + for v in rv { + if lv.iter().all(|i| *i != v) { + variables.push(v) + } + } + let iter = Box::new(li.chain(ri)); + Ok(Bindings { variables, iter }) + } + fn extend( &mut self, inner: &GraphPattern, diff --git a/sparql/src/test.rs b/sparql/src/test.rs index e9dda63..73457ff 100644 --- a/sparql/src/test.rs +++ b/sparql/src/test.rs @@ -19,6 +19,11 @@ use test_case::test_case; vec![]; "no result" )] +#[test_case( + "PREFIX s: SELECT ?x { { ?x a s:Event } UNION { ?x a s:Person } }", + vec!["", "_:b"]; + "union" +)] fn test_select_1_and_ask(query: &str, exp: Vec<&str>) -> TestResult { let dataset = dataset_101()?; let dataset = SparqlWrapper(&dataset); @@ -35,6 +40,39 @@ fn test_select_1_and_ask(query: &str, exp: Vec<&str>) -> TestResult { Ok(()) } +#[test] +fn test_union() -> TestResult { + let dataset = dataset_101()?; + let dataset = SparqlWrapper(&dataset); + let parsed_query = SparqlQuery::parse( + r" + PREFIX s: + SELECT ?p ?e { + { ?p a s:Person } + UNION + { ?e a s:Event } + } + ", + )?; + let bindings = dataset.query(&parsed_query)?.into_bindings(); + assert_eq!(bindings.variables(), &["p", "e"]); + let mut got = bindings.into_iter().collect::, _>>()?; + got.sort(); + assert_eq!(got.len(), 2); + assert_eq!(got[0].len(), 2); + assert!(got[0][0].is_none()); + assert!(got[0][1].is_some()); + assert!(got[0][1].as_ref().unwrap().is_blank_node()); + assert!(got[1][0].is_some()); + assert!(got[1][0].as_ref().unwrap().is_iri()); + assert_eq!( + got[1][0].as_ref().unwrap().to_string(), + "" + ); + assert!(got[1][1].is_none()); + Ok(()) +} + #[test_case(1)] #[test_case(2)] #[test_case(3)]