Provides a nice API for SPARQL SELECT solutions handling

pull/35/head
Tpt 4 years ago
parent 2866e1e3bd
commit cc7c92092f
  1. 18
      js/src/store.rs
  2. 5
      lib/src/lib.rs
  3. 1
      lib/src/model/named_node.rs
  4. 41
      lib/src/sparql/eval.rs
  5. 68
      lib/src/sparql/json_results.rs
  6. 19
      lib/src/sparql/mod.rs
  7. 145
      lib/src/sparql/model.rs
  8. 83
      lib/src/sparql/xml_results.rs
  9. 10
      lib/src/store/memory.rs
  10. 4
      lib/src/store/rocksdb.rs
  11. 5
      lib/src/store/sled.rs
  12. 93
      lib/tests/service_test_cases.rs
  13. 41
      lib/tests/sparql_test_cases.rs
  14. 12
      lib/tests/wasm.rs

@ -116,18 +116,16 @@ impl JsMemoryStore {
.map_err(to_err)?; .map_err(to_err)?;
let results = query.exec().map_err(to_err)?; let results = query.exec().map_err(to_err)?;
let output = match results { let output = match results {
QueryResult::Bindings(bindings) => { QueryResult::Bindings(solutions) => {
let (variables, iter) = bindings.destruct();
let variables: Vec<JsValue> =
variables.into_iter().map(|v| v.as_str().into()).collect();
let results = Array::new(); let results = Array::new();
for values in iter { for solution in solutions {
let values = values.map_err(to_err)?; let solution = solution.map_err(to_err)?;
let result = Map::new(); let result = Map::new();
for (variable, value) in variables.iter().zip(values) { for (variable, value) in solution.iter() {
if let Some(value) = value { result.set(
result.set(variable, &JsTerm::from(value).into()); &variable.as_str().into(),
} &JsTerm::from(value.clone()).into(),
);
} }
results.push(&result.into()); results.push(&result.into());
} }

@ -33,9 +33,8 @@
//! //!
//! // SPARQL query //! // SPARQL query
//! let prepared_query = store.prepare_query("SELECT ?s WHERE { ?s ?p ?o }", QueryOptions::default())?; //! let prepared_query = store.prepare_query("SELECT ?s WHERE { ?s ?p ?o }", QueryOptions::default())?;
//! let results = prepared_query.exec()?; //! if let QueryResult::Bindings(mut solutions) = prepared_query.exec()? {
//! if let QueryResult::Bindings(results) = results { //! assert_eq!(solutions.next().unwrap()?.get("s"), Some(&ex.into()));
//! assert_eq!(results.into_values_iter().next().unwrap()?[0], Some(ex.into()));
//! } //! }
//! # Result::Ok(()) //! # Result::Ok(())
//! ``` //! ```

@ -13,7 +13,6 @@ use std::fmt;
/// NamedNode::parse("http://example.com/foo").unwrap().to_string() /// NamedNode::parse("http://example.com/foo").unwrap().to_string()
/// ) /// )
/// ``` /// ```
///
#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Hash)] #[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Hash)]
pub struct NamedNode { pub struct NamedNode {
iri: String, iri: String,

@ -1564,13 +1564,13 @@ impl<'a, S: ReadableEncodedStore + 'a> SimpleEvaluator<S> {
&'b self, &'b self,
iter: EncodedTuplesIterator<'b>, iter: EncodedTuplesIterator<'b>,
variables: Vec<Variable>, variables: Vec<Variable>,
) -> BindingsIterator<'b> ) -> QuerySolutionsIterator<'b>
where where
'a: 'b, 'a: 'b,
{ {
let eval = self; let eval = self;
let tuple_size = variables.len(); let tuple_size = variables.len();
BindingsIterator::new( QuerySolutionsIterator::new(
variables, variables,
Box::new(iter.map(move |values| { Box::new(iter.map(move |values| {
let mut result = vec![None; tuple_size]; let mut result = vec![None; tuple_size];
@ -1584,40 +1584,25 @@ impl<'a, S: ReadableEncodedStore + 'a> SimpleEvaluator<S> {
) )
} }
// this is used to encode results froma BindingIterator into an EncodedTuplesIterator. This happens when SERVICE clauses are evaluated // this is used to encode results from a BindingIterator into an EncodedTuplesIterator. This happens when SERVICE clauses are evaluated
fn encode_bindings<'b>( fn encode_bindings<'b>(
&'b self, &'b self,
variables: &'b [Variable], variables: &'b [Variable],
iter: BindingsIterator<'b>, iter: QuerySolutionsIterator<'b>,
) -> EncodedTuplesIterator<'b> ) -> EncodedTuplesIterator<'b>
where where
'a: 'b, 'a: 'b,
{ {
let (binding_variables, iter) = BindingsIterator::destruct(iter); Box::new(iter.map(move |solution| {
let mut combined_variables = variables.to_vec();
for v in binding_variables.clone() {
if !combined_variables.contains(&v) {
combined_variables.resize(combined_variables.len() + 1, v);
}
}
Box::new(iter.map(move |terms| {
let mut encoder = self.dataset.encoder(); let mut encoder = self.dataset.encoder();
let mut encoded_terms = EncodedTuple::with_capacity(combined_variables.len()); let mut encoded_terms = EncodedTuple::with_capacity(variables.len());
for (i, term_option) in terms?.into_iter().enumerate() { for (variable, term) in solution?.iter() {
match term_option { put_variable_value(
None => (), variable,
Some(term) => { variables,
if let Ok(encoded) = encoder.encode_term(&term) { encoder.encode_term(term)?,
let variable = binding_variables[i].clone(); &mut encoded_terms,
put_variable_value( )
&variable,
&combined_variables,
encoded,
&mut encoded_terms,
)
}
}
}
} }
Ok(encoded_terms) Ok(encoded_terms)
})) }))

@ -13,11 +13,10 @@ pub fn write_json_results<W: Write>(results: QueryResult<'_>, mut sink: W) -> Re
sink.write_all(if value { b"true" } else { b"false" })?; sink.write_all(if value { b"true" } else { b"false" })?;
sink.write_all(b"}")?; sink.write_all(b"}")?;
} }
QueryResult::Bindings(bindings) => { QueryResult::Bindings(solutions) => {
let (variables, results) = bindings.destruct();
sink.write_all(b"{\"head\":{\"vars\":[")?; sink.write_all(b"{\"head\":{\"vars\":[")?;
let mut start_vars = true; let mut start_vars = true;
for variable in &variables { for variable in solutions.variables() {
if start_vars { if start_vars {
start_vars = false; start_vars = false;
} else { } else {
@ -27,7 +26,7 @@ pub fn write_json_results<W: Write>(results: QueryResult<'_>, mut sink: W) -> Re
} }
sink.write_all(b"]},\"results\":{\"bindings\":[")?; sink.write_all(b"]},\"results\":{\"bindings\":[")?;
let mut start_bindings = true; let mut start_bindings = true;
for result in results { for solution in solutions {
if start_bindings { if start_bindings {
start_bindings = false; start_bindings = false;
} else { } else {
@ -35,42 +34,37 @@ pub fn write_json_results<W: Write>(results: QueryResult<'_>, mut sink: W) -> Re
} }
sink.write_all(b"{")?; sink.write_all(b"{")?;
let result = result?; let solution = solution?;
let mut start_binding = true; let mut start_binding = true;
for (i, value) in result.into_iter().enumerate() { for (variable, value) in solution.iter() {
if let Some(term) = value { if start_binding {
if start_binding { start_binding = false;
start_binding = false; } else {
} else { sink.write_all(b",")?;
sink.write_all(b",")?; }
write_escaped_json_string(variable.as_str(), &mut sink)?;
match value {
Term::NamedNode(uri) => {
sink.write_all(b":{\"type\":\"uri\",\"value\":")?;
write_escaped_json_string(uri.as_str(), &mut sink)?;
sink.write_all(b"}")?;
} }
write_escaped_json_string(variables[i].as_str(), &mut sink)?; Term::BlankNode(bnode) => {
match term { sink.write_all(b":{\"type\":\"bnode\",\"value\":")?;
Term::NamedNode(uri) => { write!(sink, "{}", bnode.as_str())?;
sink.write_all(b":{\"type\":\"uri\",\"value\":")?; sink.write_all(b"}")?;
write_escaped_json_string(uri.as_str(), &mut sink)?; }
sink.write_all(b"}")?; Term::Literal(literal) => {
} sink.write_all(b":{\"type\":\"literal\",\"value\":")?;
Term::BlankNode(bnode) => { write_escaped_json_string(literal.value(), &mut sink)?;
sink.write_all(b":{\"type\":\"bnode\",\"value\":")?; if let Some(language) = literal.language() {
write!(sink, "{}", bnode.as_str())?; sink.write_all(b",\"xml:lang\":")?;
sink.write_all(b"}")?; write_escaped_json_string(language, &mut sink)?;
} } else if !literal.is_plain() {
Term::Literal(literal) => { sink.write_all(b",\"datatype\":")?;
sink.write_all(b":{\"type\":\"literal\",\"value\":")?; write_escaped_json_string(literal.datatype().as_str(), &mut sink)?;
write_escaped_json_string(literal.value(), &mut sink)?;
if let Some(language) = literal.language() {
sink.write_all(b",\"xml:lang\":")?;
write_escaped_json_string(language, &mut sink)?;
} else if !literal.is_plain() {
sink.write_all(b",\"datatype\":")?;
write_escaped_json_string(
literal.datatype().as_str(),
&mut sink,
)?;
}
sink.write_all(b"}")?;
} }
sink.write_all(b"}")?;
} }
} }
} }

@ -21,7 +21,10 @@ use crate::Result;
use oxiri::Iri; use oxiri::Iri;
pub use crate::sparql::algebra::GraphPattern; pub use crate::sparql::algebra::GraphPattern;
pub use crate::sparql::model::BindingsIterator; pub use crate::sparql::model::QuerySolution;
pub use crate::sparql::model::QuerySolutionsIterator;
#[deprecated(note = "Please directly use QuerySolutionsIterator type instead")]
pub type BindingsIterator<'a> = QuerySolutionsIterator<'a>;
pub use crate::sparql::model::QueryResult; pub use crate::sparql::model::QueryResult;
pub use crate::sparql::model::QueryResultSyntax; pub use crate::sparql::model::QueryResultSyntax;
pub use crate::sparql::model::Variable; pub use crate::sparql::model::Variable;
@ -161,17 +164,17 @@ pub trait ServiceHandler {
&'a self, &'a self,
service_name: &NamedNode, service_name: &NamedNode,
graph_pattern: &'a GraphPattern, graph_pattern: &'a GraphPattern,
) -> Result<BindingsIterator<'a>>; ) -> Result<QuerySolutionsIterator<'a>>;
} }
impl<F: for<'a> Fn(&NamedNode, &'a GraphPattern) -> Result<BindingsIterator<'a>>> ServiceHandler impl<F: for<'a> Fn(&NamedNode, &'a GraphPattern) -> Result<QuerySolutionsIterator<'a>>>
for F ServiceHandler for F
{ {
fn handle<'a>( fn handle<'a>(
&'a self, &'a self,
service_name: &NamedNode, service_name: &NamedNode,
graph_pattern: &'a GraphPattern, graph_pattern: &'a GraphPattern,
) -> Result<BindingsIterator<'a>> { ) -> Result<QuerySolutionsIterator<'a>> {
self(service_name, graph_pattern) self(service_name, graph_pattern)
} }
} }
@ -179,7 +182,11 @@ impl<F: for<'a> Fn(&NamedNode, &'a GraphPattern) -> Result<BindingsIterator<'a>>
struct EmptyServiceHandler; struct EmptyServiceHandler;
impl ServiceHandler for EmptyServiceHandler { impl ServiceHandler for EmptyServiceHandler {
fn handle<'a>(&'a self, _: &NamedNode, _: &'a GraphPattern) -> Result<BindingsIterator<'a>> { fn handle<'a>(
&'a self,
_: &NamedNode,
_: &'a GraphPattern,
) -> Result<QuerySolutionsIterator<'a>> {
Err(Error::msg("The SERVICE feature is not implemented")) Err(Error::msg("The SERVICE feature is not implemented"))
} }
} }

@ -9,11 +9,12 @@ use rio_turtle::{NTriplesFormatter, TurtleFormatter};
use rio_xml::RdfXmlFormatter; use rio_xml::RdfXmlFormatter;
use std::fmt; use std::fmt;
use std::io::{BufRead, Write}; use std::io::{BufRead, Write};
use std::rc::Rc;
/// Results of a [SPARQL query](https://www.w3.org/TR/sparql11-query/) /// Results of a [SPARQL query](https://www.w3.org/TR/sparql11-query/)
pub enum QueryResult<'a> { pub enum QueryResult<'a> {
/// Results of a [SELECT](https://www.w3.org/TR/sparql11-query/#select) query /// Results of a [SELECT](https://www.w3.org/TR/sparql11-query/#select) query
Bindings(BindingsIterator<'a>), Bindings(QuerySolutionsIterator<'a>),
/// Result of a [ASK](https://www.w3.org/TR/sparql11-query/#ask) query /// Result of a [ASK](https://www.w3.org/TR/sparql11-query/#ask) query
Boolean(bool), Boolean(bool),
/// Results of a [CONSTRUCT](https://www.w3.org/TR/sparql11-query/#construct) or [DESCRIBE](https://www.w3.org/TR/sparql11-query/#describe) query /// Results of a [CONSTRUCT](https://www.w3.org/TR/sparql11-query/#construct) or [DESCRIBE](https://www.w3.org/TR/sparql11-query/#describe) query
@ -119,24 +120,55 @@ impl FileSyntax for QueryResultSyntax {
} }
} }
/// An iterator over results bindings /// An iterator over query result solutions
pub struct BindingsIterator<'a> { ///
variables: Vec<Variable>, /// ```
/// use oxigraph::{MemoryStore, Result};
/// use oxigraph::sparql::{PreparedQuery, QueryResult, QueryOptions, Variable};
///
/// let store = MemoryStore::new();
/// let prepared_query = store.prepare_query("SELECT ?s WHERE { ?s ?p ?o }", QueryOptions::default())?;
/// if let QueryResult::Bindings(solutions) = prepared_query.exec()? {
/// for solution in solutions {
/// println!("{:?}", solution?.get("s"));
/// }
/// }
/// # Result::Ok(())
/// ```
pub struct QuerySolutionsIterator<'a> {
variables: Rc<Vec<Variable>>,
iter: Box<dyn Iterator<Item = Result<Vec<Option<Term>>>> + 'a>, iter: Box<dyn Iterator<Item = Result<Vec<Option<Term>>>> + 'a>,
} }
impl<'a> BindingsIterator<'a> { impl<'a> QuerySolutionsIterator<'a> {
pub fn new( pub fn new(
variables: Vec<Variable>, variables: Vec<Variable>,
iter: Box<dyn Iterator<Item = Result<Vec<Option<Term>>>> + 'a>, iter: Box<dyn Iterator<Item = Result<Vec<Option<Term>>>> + 'a>,
) -> Self { ) -> Self {
Self { variables, iter } Self {
variables: Rc::new(variables),
iter,
}
} }
/// The variables used in the solutions
///
/// ```
/// use oxigraph::{MemoryStore, Result};
/// use oxigraph::sparql::{PreparedQuery, QueryResult, QueryOptions, Variable};
///
/// let store = MemoryStore::new();
/// let prepared_query = store.prepare_query("SELECT ?s ?o WHERE { ?s ?p ?o }", QueryOptions::default())?;
/// if let QueryResult::Bindings(solutions) = prepared_query.exec()? {
/// assert_eq!(solutions.variables(), &[Variable::new("s"), Variable::new("o")]);
/// }
/// # Result::Ok(())
/// ```
pub fn variables(&self) -> &[Variable] { pub fn variables(&self) -> &[Variable] {
&*self.variables &*self.variables
} }
#[deprecated(note = "Please directly use QuerySolutionsIterator as an iterator instead")]
pub fn into_values_iter(self) -> Box<dyn Iterator<Item = Result<Vec<Option<Term>>>> + 'a> { pub fn into_values_iter(self) -> Box<dyn Iterator<Item = Result<Vec<Option<Term>>>> + 'a> {
self.iter self.iter
} }
@ -147,11 +179,108 @@ impl<'a> BindingsIterator<'a> {
Vec<Variable>, Vec<Variable>,
Box<dyn Iterator<Item = Result<Vec<Option<Term>>>> + 'a>, Box<dyn Iterator<Item = Result<Vec<Option<Term>>>> + 'a>,
) { ) {
(self.variables, self.iter) ((*self.variables).clone(), self.iter)
}
}
impl<'a> Iterator for QuerySolutionsIterator<'a> {
type Item = Result<QuerySolution>;
fn next(&mut self) -> Option<Result<QuerySolution>> {
Some(self.iter.next()?.map(|values| QuerySolution {
values,
variables: self.variables.clone(),
}))
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}
}
/// Tuple associating variables and terms that are the result of a SPARQL query.
///
/// It is the equivalent of a row in SQL.
pub struct QuerySolution {
values: Vec<Option<Term>>,
variables: Rc<Vec<Variable>>,
}
impl QuerySolution {
/// Returns a value for a given position in the tuple (`usize`) or a given variable name (`&str` or `Variable`)
///
/// ```ignore
/// let foo = solution.get("foo"); // Get the value of the variable ?foo if it exists
/// let first = solution.get(1); // Get the value of the second column if it exists
/// ```
pub fn get(&self, index: impl VariableSolutionIndex) -> Option<&Term> {
self.values.get(index.index(self)?).and_then(|e| e.as_ref())
}
/// The number of variables which are bind
pub fn len(&self) -> usize {
self.values.len()
}
/// Is this binding empty?
pub fn is_empty(&self) -> bool {
self.values.is_empty()
}
/// Returns an iterator over bound variables
pub fn iter(&self) -> impl Iterator<Item = (&Variable, &Term)> {
self.values
.iter()
.enumerate()
.filter_map(move |(i, value)| {
if let Some(value) = value {
Some((&self.variables[i], value))
} else {
None
}
})
}
}
/// A utility trait to get values for a given variable or tuple position
pub trait VariableSolutionIndex {
fn index(self, solution: &QuerySolution) -> Option<usize>;
}
impl VariableSolutionIndex for usize {
fn index(self, _: &QuerySolution) -> Option<usize> {
Some(self)
}
}
impl VariableSolutionIndex for &str {
fn index(self, solution: &QuerySolution) -> Option<usize> {
solution.variables.iter().position(|v| v.as_str() == self)
}
}
impl VariableSolutionIndex for &Variable {
fn index(self, solution: &QuerySolution) -> Option<usize> {
solution.variables.iter().position(|v| v == self)
}
}
impl VariableSolutionIndex for Variable {
fn index(self, solution: &QuerySolution) -> Option<usize> {
(&self).index(solution)
} }
} }
/// A SPARQL query variable /// A SPARQL query variable
///
/// ```
/// use oxigraph::sparql::Variable;
///
/// assert_eq!(
/// "?foo",
/// Variable::new("foo").to_string()
/// )
/// ```
#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Hash)] #[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Hash)]
pub struct Variable { pub struct Variable {
name: String, name: String,
@ -166,7 +295,7 @@ impl Variable {
&self.name &self.name
} }
#[deprecated] #[deprecated(note = "Please use as_str instead")]
pub fn name(&self) -> Result<&str> { pub fn name(&self) -> Result<&str> {
Ok(self.as_str()) Ok(self.as_str())
} }

@ -35,63 +35,58 @@ pub fn write_xml_results<W: Write>(results: QueryResult<'_>, sink: W) -> Result<
writer.write_event(Event::End(BytesEnd::borrowed(b"boolean")))?; writer.write_event(Event::End(BytesEnd::borrowed(b"boolean")))?;
writer.write_event(Event::End(BytesEnd::borrowed(b"sparql")))?; writer.write_event(Event::End(BytesEnd::borrowed(b"sparql")))?;
} }
QueryResult::Bindings(bindings) => { QueryResult::Bindings(solutions) => {
let (variables, results) = bindings.destruct();
writer.write_event(Event::Decl(BytesDecl::new(b"1.0", None, None)))?; writer.write_event(Event::Decl(BytesDecl::new(b"1.0", None, None)))?;
let mut sparql_open = BytesStart::borrowed_name(b"sparql"); let mut sparql_open = BytesStart::borrowed_name(b"sparql");
sparql_open.push_attribute(("xmlns", "http://www.w3.org/2005/sparql-results#")); sparql_open.push_attribute(("xmlns", "http://www.w3.org/2005/sparql-results#"));
writer.write_event(Event::Start(sparql_open))?; writer.write_event(Event::Start(sparql_open))?;
writer.write_event(Event::Start(BytesStart::borrowed_name(b"head")))?; writer.write_event(Event::Start(BytesStart::borrowed_name(b"head")))?;
for variable in &variables { for variable in solutions.variables() {
let mut variable_tag = BytesStart::borrowed_name(b"variable"); let mut variable_tag = BytesStart::borrowed_name(b"variable");
variable_tag.push_attribute(("name", variable.as_str())); variable_tag.push_attribute(("name", variable.as_str()));
writer.write_event(Event::Empty(variable_tag))?; writer.write_event(Event::Empty(variable_tag))?;
} }
writer.write_event(Event::End(BytesEnd::borrowed(b"head")))?; writer.write_event(Event::End(BytesEnd::borrowed(b"head")))?;
writer.write_event(Event::Start(BytesStart::borrowed_name(b"results")))?; writer.write_event(Event::Start(BytesStart::borrowed_name(b"results")))?;
for result in results { for solution in solutions {
let result = result?; let solution = solution?;
writer.write_event(Event::Start(BytesStart::borrowed_name(b"result")))?; writer.write_event(Event::Start(BytesStart::borrowed_name(b"result")))?;
for (i, value) in result.into_iter().enumerate() { for (variable, value) in solution.iter() {
if let Some(term) = value { let mut binding_tag = BytesStart::borrowed_name(b"binding");
let mut binding_tag = BytesStart::borrowed_name(b"binding"); binding_tag.push_attribute(("name", variable.as_str()));
binding_tag.push_attribute(("name", variables[i].as_str())); writer.write_event(Event::Start(binding_tag))?;
writer.write_event(Event::Start(binding_tag))?; match value {
match term { Term::NamedNode(uri) => {
Term::NamedNode(uri) => { writer.write_event(Event::Start(BytesStart::borrowed_name(b"uri")))?;
writer writer.write_event(Event::Text(BytesText::from_plain_str(
.write_event(Event::Start(BytesStart::borrowed_name(b"uri")))?; uri.as_str(),
writer.write_event(Event::Text(BytesText::from_plain_str( )))?;
uri.as_str(), writer.write_event(Event::End(BytesEnd::borrowed(b"uri")))?;
)))?; }
writer.write_event(Event::End(BytesEnd::borrowed(b"uri")))?; Term::BlankNode(bnode) => {
} writer
Term::BlankNode(bnode) => { .write_event(Event::Start(BytesStart::borrowed_name(b"bnode")))?;
writer.write_event(Event::Start(BytesStart::borrowed_name( writer.write_event(Event::Text(BytesText::from_plain_str(
b"bnode", bnode.as_str(),
)))?; )))?;
writer.write_event(Event::Text(BytesText::from_plain_str( writer.write_event(Event::End(BytesEnd::borrowed(b"bnode")))?;
bnode.as_str(), }
)))?; Term::Literal(literal) => {
writer.write_event(Event::End(BytesEnd::borrowed(b"bnode")))?; let mut literal_tag = BytesStart::borrowed_name(b"literal");
} if let Some(language) = literal.language() {
Term::Literal(literal) => { literal_tag.push_attribute(("xml:lang", language));
let mut literal_tag = BytesStart::borrowed_name(b"literal"); } else if !literal.is_plain() {
if let Some(language) = literal.language() { literal_tag
literal_tag.push_attribute(("xml:lang", language)); .push_attribute(("datatype", literal.datatype().as_str()));
} else if !literal.is_plain() {
literal_tag
.push_attribute(("datatype", literal.datatype().as_str()));
}
writer.write_event(Event::Start(literal_tag))?;
writer.write_event(Event::Text(BytesText::from_plain_str(
literal.value(),
)))?;
writer.write_event(Event::End(BytesEnd::borrowed(b"literal")))?;
} }
writer.write_event(Event::Start(literal_tag))?;
writer.write_event(Event::Text(BytesText::from_plain_str(
literal.value(),
)))?;
writer.write_event(Event::End(BytesEnd::borrowed(b"literal")))?;
} }
writer.write_event(Event::End(BytesEnd::borrowed(b"binding")))?;
} }
writer.write_event(Event::End(BytesEnd::borrowed(b"binding")))?;
} }
writer.write_event(Event::End(BytesEnd::borrowed(b"result")))?; writer.write_event(Event::End(BytesEnd::borrowed(b"result")))?;
} }
@ -175,7 +170,7 @@ pub fn read_xml_results<'a>(source: impl BufRead + 'a) -> Result<QueryResult<'a>
for (i,var) in variables.iter().enumerate() { for (i,var) in variables.iter().enumerate() {
mapping.insert(var.as_bytes().to_vec(), i); mapping.insert(var.as_bytes().to_vec(), i);
} }
return Ok(QueryResult::Bindings(BindingsIterator::new( return Ok(QueryResult::Bindings(QuerySolutionsIterator::new(
variables.into_iter().map(Variable::new).collect(), variables.into_iter().map(Variable::new).collect(),
Box::new(ResultsIterator { Box::new(ResultsIterator {
reader, reader,
@ -214,7 +209,7 @@ pub fn read_xml_results<'a>(source: impl BufRead + 'a) -> Result<QueryResult<'a>
}, },
State::AfterHead => { State::AfterHead => {
if event.name() == b"results" { if event.name() == b"results" {
return Ok(QueryResult::Bindings(BindingsIterator::new( return Ok(QueryResult::Bindings(QuerySolutionsIterator::new(
variables.into_iter().map(Variable::new).collect(), variables.into_iter().map(Variable::new).collect(),
Box::new(empty()), Box::new(empty()),
))) )))

@ -31,9 +31,8 @@ use std::sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard};
/// ///
/// // SPARQL query /// // SPARQL query
/// let prepared_query = store.prepare_query("SELECT ?s WHERE { ?s ?p ?o }", QueryOptions::default())?; /// let prepared_query = store.prepare_query("SELECT ?s WHERE { ?s ?p ?o }", QueryOptions::default())?;
/// let results = prepared_query.exec()?; /// if let QueryResult::Bindings(mut solutions) = prepared_query.exec()? {
/// if let QueryResult::Bindings(results) = results { /// assert_eq!(solutions.next().unwrap()?.get("s"), Some(&ex.into()));
/// assert_eq!(results.into_values_iter().next().unwrap()?[0], Some(ex.into()));
/// } /// }
/// # Result::Ok(()) /// # Result::Ok(())
/// ``` /// ```
@ -88,9 +87,8 @@ impl MemoryStore {
/// ///
/// // SPARQL query /// // SPARQL query
/// let prepared_query = store.prepare_query("SELECT ?s WHERE { ?s ?p ?o }", QueryOptions::default())?; /// let prepared_query = store.prepare_query("SELECT ?s WHERE { ?s ?p ?o }", QueryOptions::default())?;
/// let results = prepared_query.exec()?; /// if let QueryResult::Bindings(mut solutions) = prepared_query.exec()? {
/// if let QueryResult::Bindings(results) = results { /// assert_eq!(solutions.next().unwrap()?.get("s"), Some(&ex.into()));
/// assert_eq!(results.into_values_iter().next().unwrap()?[0], Some(ex.into()));
/// } /// }
/// # Result::Ok(()) /// # Result::Ok(())
/// ``` /// ```

@ -37,8 +37,8 @@ use std::sync::Arc;
/// // SPARQL query /// // SPARQL query
/// let prepared_query = store.prepare_query("SELECT ?s WHERE { ?s ?p ?o }", QueryOptions::default())?; /// let prepared_query = store.prepare_query("SELECT ?s WHERE { ?s ?p ?o }", QueryOptions::default())?;
/// let results = prepared_query.exec()?; /// let results = prepared_query.exec()?;
/// if let QueryResult::Bindings(results) = results { /// if let QueryResult::Bindings(mut solutions) = results {
/// assert_eq!(results.into_values_iter().next().unwrap()?[0], Some(ex.into())); /// assert_eq!(solutions.next().unwrap()?.get("s"), Some(&ex.into()));
/// } /// }
/// # /// #
/// # } /// # }

@ -36,9 +36,8 @@ use std::str;
/// ///
/// // SPARQL query /// // SPARQL query
/// let prepared_query = store.prepare_query("SELECT ?s WHERE { ?s ?p ?o }", QueryOptions::default())?; /// let prepared_query = store.prepare_query("SELECT ?s WHERE { ?s ?p ?o }", QueryOptions::default())?;
/// let results = prepared_query.exec()?; /// if let QueryResult::Bindings(mut solutions) = prepared_query.exec()? {
/// if let QueryResult::Bindings(results) = results { /// assert_eq!(solutions.next().unwrap()?.get("s"), Some(&ex.into()));
/// assert_eq!(results.into_values_iter().next().unwrap()?[0], Some(ex.into()));
/// } /// }
/// # /// #
/// # } /// # }

@ -11,7 +11,7 @@ fn simple_service_test() {
&'a self, &'a self,
_: &NamedNode, _: &NamedNode,
graph_pattern: &'a GraphPattern, graph_pattern: &'a GraphPattern,
) -> Result<BindingsIterator<'a>> { ) -> Result<QuerySolutionsIterator<'a>> {
let triples = let triples =
b"<http://example.com/s> <http://example.com/p> <http://example.com/o> .".as_ref(); b"<http://example.com/s> <http://example.com/p> <http://example.com/o> .".as_ref();
do_pattern(triples, graph_pattern, QueryOptions::default()) do_pattern(triples, graph_pattern, QueryOptions::default())
@ -30,16 +30,16 @@ fn simple_service_test() {
.to_string(); .to_string();
let options = QueryOptions::default().with_service_handler(TestServiceHandler); let options = QueryOptions::default().with_service_handler(TestServiceHandler);
let results = do_query(b"".as_ref(), query, options).unwrap(); let collected = do_query(b"".as_ref(), query, options)
let collected = results .unwrap()
.into_values_iter() .map(|b| {
.map(move |b| b.unwrap()) b.unwrap()
.iter()
.map(|(_, v)| v.clone())
.collect::<Vec<_>>()
})
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let solution = vec![vec![ let solution = vec![vec![ex("s"), ex("p"), ex("o")]];
Some(ex(String::from("s"))),
Some(ex(String::from("p"))),
Some(ex(String::from("o"))),
]];
assert_eq!(collected, solution); assert_eq!(collected, solution);
} }
@ -52,7 +52,7 @@ fn two_service_test() {
&'a self, &'a self,
named_node: &NamedNode, named_node: &NamedNode,
graph_pattern: &'a GraphPattern, graph_pattern: &'a GraphPattern,
) -> Result<BindingsIterator<'a>> { ) -> Result<QuerySolutionsIterator<'a>> {
let service1 = NamedNode::parse("http://service1.org").unwrap(); let service1 = NamedNode::parse("http://service1.org").unwrap();
let service2 = NamedNode::parse("http://service2.org").unwrap(); let service2 = NamedNode::parse("http://service2.org").unwrap();
if named_node == &service1 { if named_node == &service1 {
@ -93,20 +93,18 @@ fn two_service_test() {
.to_string(); .to_string();
let options = QueryOptions::default().with_service_handler(TwoServiceTest); let options = QueryOptions::default().with_service_handler(TwoServiceTest);
let results = do_query(b"".as_ref(), query, options).unwrap(); let collected = do_query(b"".as_ref(), query, options)
let collected = results .unwrap()
.into_values_iter() .map(|b| {
.map(move |b| b.unwrap()) b.unwrap()
.iter()
.map(|(_, v)| v.clone())
.collect::<Vec<_>>()
})
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let solution = vec![ let solution = vec![
vec![ vec![literal("Alice"), mailto("alice@example.com")],
Some(literal("Alice".to_string())), vec![literal("Bob"), mailto("bob@example.com")],
Some(mailto("alice@example.com".to_string())),
],
vec![
Some(literal("Bob".to_string())),
Some(mailto("bob@example.com".to_string())),
],
]; ];
assert_eq!(collected, solution); assert_eq!(collected, solution);
} }
@ -120,7 +118,7 @@ fn silent_service_empty_set_test() {
&'a self, &'a self,
_: &NamedNode, _: &NamedNode,
_: &'a GraphPattern, _: &'a GraphPattern,
) -> Result<BindingsIterator<'a>> { ) -> Result<QuerySolutionsIterator<'a>> {
Err(Error::msg("This is supposed to fail")) Err(Error::msg("This is supposed to fail"))
} }
} }
@ -141,12 +139,7 @@ fn silent_service_empty_set_test() {
let triples = b"".as_ref(); let triples = b"".as_ref();
let options = QueryOptions::default().with_service_handler(ServiceTest); let options = QueryOptions::default().with_service_handler(ServiceTest);
let results = do_query(triples, query, options).unwrap(); assert_eq!(do_query(triples, query, options).unwrap().count(), 1);
let collected = results
.into_values_iter()
.map(move |b| b.unwrap())
.collect::<Vec<_>>();
assert_eq!(collected.len(), 1);
} }
#[test] #[test]
@ -158,7 +151,7 @@ fn non_silent_service_test() {
&'a self, &'a self,
_: &NamedNode, _: &NamedNode,
_: &'a GraphPattern, _: &'a GraphPattern,
) -> Result<BindingsIterator<'a>> { ) -> Result<QuerySolutionsIterator<'a>> {
Err(Error::msg("This is supposed to fail")) Err(Error::msg("This is supposed to fail"))
} }
} }
@ -179,26 +172,22 @@ fn non_silent_service_test() {
let triples = b"".as_ref(); let triples = b"".as_ref();
let options = QueryOptions::default().with_service_handler(ServiceTest); let options = QueryOptions::default().with_service_handler(ServiceTest);
let results = do_query(triples, query, options).unwrap(); let mut solutions = do_query(triples, query, options).unwrap();
let result = results.into_values_iter().next(); if let Some(Err(_)) = solutions.next() {
match result { } else {
Some(Err(_)) => assert_eq!(true, true), panic!("This should have been an error since the service fails")
_ => assert_eq!(
true, false,
"This should have been an error since the service fails"
),
} }
} }
fn ex(id: String) -> Term { fn ex(id: &str) -> Term {
Term::NamedNode(NamedNode::parse(format!("http://example.com/{}", &id)).unwrap()) Term::NamedNode(NamedNode::parse(format!("http://example.com/{}", id)).unwrap())
} }
fn mailto(id: String) -> Term { fn mailto(id: &str) -> Term {
Term::NamedNode(NamedNode::parse(format!("mailto:{}", &id)).unwrap()) Term::NamedNode(NamedNode::parse(format!("mailto:{}", id)).unwrap())
} }
fn literal(str: String) -> Term { fn literal(str: &str) -> Term {
Term::Literal(Literal::new_simple_literal(str)) Term::Literal(Literal::new_simple_literal(str))
} }
@ -214,13 +203,13 @@ fn query_store<'a>(
store: MemoryStore, store: MemoryStore,
query: String, query: String,
options: QueryOptions<'a>, options: QueryOptions<'a>,
) -> Result<BindingsIterator<'a>> { ) -> Result<QuerySolutionsIterator<'a>> {
match store.prepare_query(&query, options)?.exec()? { match store.prepare_query(&query, options)?.exec()? {
QueryResult::Bindings(iterator) => { QueryResult::Bindings(iterator) => {
let (varaibles, iter) = iterator.destruct(); let (variables, iter) = iterator.destruct();
let collected = iter.collect::<Vec<_>>(); let collected = iter.collect::<Vec<_>>();
Ok(BindingsIterator::new( Ok(QuerySolutionsIterator::new(
varaibles, variables,
Box::new(collected.into_iter()), Box::new(collected.into_iter()),
)) ))
} }
@ -232,7 +221,7 @@ fn pattern_store<'a>(
store: MemoryStore, store: MemoryStore,
pattern: &'a GraphPattern, pattern: &'a GraphPattern,
options: QueryOptions<'a>, options: QueryOptions<'a>,
) -> Result<BindingsIterator<'a>> { ) -> Result<QuerySolutionsIterator<'a>> {
match store match store
.prepare_query_from_pattern(&pattern, options)? .prepare_query_from_pattern(&pattern, options)?
.exec()? .exec()?
@ -240,7 +229,7 @@ fn pattern_store<'a>(
QueryResult::Bindings(iterator) => { QueryResult::Bindings(iterator) => {
let (varaibles, iter) = iterator.destruct(); let (varaibles, iter) = iterator.destruct();
let collected = iter.collect::<Vec<_>>(); let collected = iter.collect::<Vec<_>>();
Ok(BindingsIterator::new( Ok(QuerySolutionsIterator::new(
varaibles, varaibles,
Box::new(collected.into_iter()), Box::new(collected.into_iter()),
)) ))
@ -253,7 +242,7 @@ fn do_query<'a>(
reader: impl BufRead, reader: impl BufRead,
query: String, query: String,
options: QueryOptions<'a>, options: QueryOptions<'a>,
) -> Result<BindingsIterator<'a>> { ) -> Result<QuerySolutionsIterator<'a>> {
let store = make_store(reader)?; let store = make_store(reader)?;
query_store(store, query, options) query_store(store, query, options)
} }
@ -262,7 +251,7 @@ fn do_pattern<'a>(
reader: impl BufRead, reader: impl BufRead,
pattern: &'a GraphPattern, pattern: &'a GraphPattern,
options: QueryOptions<'a>, options: QueryOptions<'a>,
) -> Result<BindingsIterator<'a>> { ) -> Result<QuerySolutionsIterator<'a>> {
let store = make_store(reader)?; let store = make_store(reader)?;
pattern_store(store, pattern, options) pattern_store(store, pattern, options)
} }

@ -348,7 +348,7 @@ fn to_graph(result: QueryResult<'_>, with_order: bool) -> Result<SimpleGraph> {
)); ));
Ok(graph) Ok(graph)
} }
QueryResult::Bindings(bindings) => { QueryResult::Bindings(solutions) => {
let mut graph = SimpleGraph::default(); let mut graph = SimpleGraph::default();
let result_set = BlankNode::default(); let result_set = BlankNode::default();
graph.insert(Triple::new( graph.insert(Triple::new(
@ -356,33 +356,30 @@ fn to_graph(result: QueryResult<'_>, with_order: bool) -> Result<SimpleGraph> {
rdf::TYPE.clone(), rdf::TYPE.clone(),
rs::RESULT_SET.clone(), rs::RESULT_SET.clone(),
)); ));
let (variables, iter) = bindings.destruct(); for variable in solutions.variables() {
for variable in &variables {
graph.insert(Triple::new( graph.insert(Triple::new(
result_set, result_set,
rs::RESULT_VARIABLE.clone(), rs::RESULT_VARIABLE.clone(),
Literal::new_simple_literal(variable.as_str()), Literal::new_simple_literal(variable.as_str()),
)); ));
} }
for (i, binding_values) in iter.enumerate() { for (i, solution) in solutions.enumerate() {
let binding_values = binding_values?; let solution = solution?;
let solution = BlankNode::default(); let solution_id = BlankNode::default();
graph.insert(Triple::new(result_set, rs::SOLUTION.clone(), solution)); graph.insert(Triple::new(result_set, rs::SOLUTION.clone(), solution_id));
for i in 0..variables.len() { for (variable, value) in solution.iter() {
if let Some(ref value) = binding_values[i] { let binding = BlankNode::default();
let binding = BlankNode::default(); graph.insert(Triple::new(solution_id, rs::BINDING.clone(), binding));
graph.insert(Triple::new(solution, rs::BINDING.clone(), binding)); graph.insert(Triple::new(binding, rs::VALUE.clone(), value.clone()));
graph.insert(Triple::new(binding, rs::VALUE.clone(), value.clone())); graph.insert(Triple::new(
graph.insert(Triple::new( binding,
binding, rs::VARIABLE.clone(),
rs::VARIABLE.clone(), Literal::new_simple_literal(variable.as_str()),
Literal::new_simple_literal(variables[i].as_str()), ));
));
}
} }
if with_order { if with_order {
graph.insert(Triple::new( graph.insert(Triple::new(
solution, solution_id,
rs::INDEX.clone(), rs::INDEX.clone(),
Literal::from((i + 1) as i128), Literal::from((i + 1) as i128),
)); ));
@ -720,7 +717,7 @@ impl ServiceHandler for StaticServiceHandler {
&'a self, &'a self,
service_name: &NamedNode, service_name: &NamedNode,
graph_pattern: &'a GraphPattern, graph_pattern: &'a GraphPattern,
) -> Result<BindingsIterator<'a>> { ) -> Result<QuerySolutionsIterator<'a>> {
if let QueryResult::Bindings(iterator) = self if let QueryResult::Bindings(iterator) = self
.services .services
.get(service_name) .get(service_name)
@ -731,10 +728,10 @@ impl ServiceHandler for StaticServiceHandler {
)? )?
.exec()? .exec()?
{ {
//TODO: very hugly //TODO: very ugly
let (variables, iter) = iterator.destruct(); let (variables, iter) = iterator.destruct();
let collected = iter.collect::<Vec<_>>(); let collected = iter.collect::<Vec<_>>();
Ok(BindingsIterator::new( Ok(QuerySolutionsIterator::new(
variables, variables,
Box::new(collected.into_iter()), Box::new(collected.into_iter()),
)) ))

@ -22,18 +22,17 @@ mod test {
let prepared_query = store let prepared_query = store
.prepare_query("SELECT ?s WHERE { ?s ?p ?o }", QueryOptions::default()) .prepare_query("SELECT ?s WHERE { ?s ?p ?o }", QueryOptions::default())
.unwrap(); .unwrap();
let results = prepared_query.exec().unwrap(); if let QueryResult::Bindings(mut solutions) = prepared_query.exec().unwrap() {
if let QueryResult::Bindings(results) = results {
assert_eq!( assert_eq!(
results.into_values_iter().next().unwrap().unwrap()[0], solutions.next().unwrap().unwrap().get("s"),
Some(ex.into()) Some(&ex.into())
); );
} }
} }
#[wasm_bindgen_test] #[wasm_bindgen_test]
fn now() { fn now() {
if let QueryResult::Bindings(results) = MemoryStore::default() if let QueryResult::Bindings(solutions) = MemoryStore::default()
.prepare_query( .prepare_query(
"SELECT (YEAR(NOW()) AS ?y) WHERE {}", "SELECT (YEAR(NOW()) AS ?y) WHERE {}",
QueryOptions::default(), QueryOptions::default(),
@ -42,8 +41,7 @@ mod test {
.exec() .exec()
.unwrap() .unwrap()
{ {
if let Some(Term::Literal(l)) = &results.into_values_iter().next().unwrap().unwrap()[0] if let Some(Term::Literal(l)) = solutions.next().unwrap().unwrap().get(0) {
{
let year = i64::from_str(l.value()).unwrap(); let year = i64::from_str(l.value()).unwrap();
assert!(2020 <= year && year <= 2100); assert!(2020 <= year && year <= 2100);
} }

Loading…
Cancel
Save