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. 33
      lib/src/sparql/eval.rs
  5. 22
      lib/src/sparql/json_results.rs
  6. 19
      lib/src/sparql/mod.rs
  7. 145
      lib/src/sparql/model.rs
  8. 29
      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. 29
      lib/tests/sparql_test_cases.rs
  14. 12
      lib/tests/wasm.rs

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

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

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

@ -1564,13 +1564,13 @@ impl<'a, S: ReadableEncodedStore + 'a> SimpleEvaluator<S> {
&'b self,
iter: EncodedTuplesIterator<'b>,
variables: Vec<Variable>,
) -> BindingsIterator<'b>
) -> QuerySolutionsIterator<'b>
where
'a: 'b,
{
let eval = self;
let tuple_size = variables.len();
BindingsIterator::new(
QuerySolutionsIterator::new(
variables,
Box::new(iter.map(move |values| {
let mut result = vec![None; tuple_size];
@ -1588,37 +1588,22 @@ impl<'a, S: ReadableEncodedStore + 'a> SimpleEvaluator<S> {
fn encode_bindings<'b>(
&'b self,
variables: &'b [Variable],
iter: BindingsIterator<'b>,
iter: QuerySolutionsIterator<'b>,
) -> EncodedTuplesIterator<'b>
where
'a: 'b,
{
let (binding_variables, iter) = BindingsIterator::destruct(iter);
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| {
Box::new(iter.map(move |solution| {
let mut encoder = self.dataset.encoder();
let mut encoded_terms = EncodedTuple::with_capacity(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();
let mut encoded_terms = EncodedTuple::with_capacity(variables.len());
for (variable, term) in solution?.iter() {
put_variable_value(
&variable,
&combined_variables,
encoded,
variable,
variables,
encoder.encode_term(term)?,
&mut 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(b"}")?;
}
QueryResult::Bindings(bindings) => {
let (variables, results) = bindings.destruct();
QueryResult::Bindings(solutions) => {
sink.write_all(b"{\"head\":{\"vars\":[")?;
let mut start_vars = true;
for variable in &variables {
for variable in solutions.variables() {
if start_vars {
start_vars = false;
} else {
@ -27,7 +26,7 @@ pub fn write_json_results<W: Write>(results: QueryResult<'_>, mut sink: W) -> Re
}
sink.write_all(b"]},\"results\":{\"bindings\":[")?;
let mut start_bindings = true;
for result in results {
for solution in solutions {
if start_bindings {
start_bindings = false;
} else {
@ -35,17 +34,16 @@ pub fn write_json_results<W: Write>(results: QueryResult<'_>, mut sink: W) -> Re
}
sink.write_all(b"{")?;
let result = result?;
let solution = solution?;
let mut start_binding = true;
for (i, value) in result.into_iter().enumerate() {
if let Some(term) = value {
for (variable, value) in solution.iter() {
if start_binding {
start_binding = false;
} else {
sink.write_all(b",")?;
}
write_escaped_json_string(variables[i].as_str(), &mut sink)?;
match term {
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)?;
@ -64,16 +62,12 @@ pub fn write_json_results<W: Write>(results: QueryResult<'_>, mut sink: W) -> Re
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,
)?;
write_escaped_json_string(literal.datatype().as_str(), &mut sink)?;
}
sink.write_all(b"}")?;
}
}
}
}
sink.write_all(b"}")?;
}
sink.write_all(b"]}}")?;

@ -21,7 +21,10 @@ use crate::Result;
use oxiri::Iri;
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::QueryResultSyntax;
pub use crate::sparql::model::Variable;
@ -161,17 +164,17 @@ pub trait ServiceHandler {
&'a self,
service_name: &NamedNode,
graph_pattern: &'a GraphPattern,
) -> Result<BindingsIterator<'a>>;
) -> Result<QuerySolutionsIterator<'a>>;
}
impl<F: for<'a> Fn(&NamedNode, &'a GraphPattern) -> Result<BindingsIterator<'a>>> ServiceHandler
for F
impl<F: for<'a> Fn(&NamedNode, &'a GraphPattern) -> Result<QuerySolutionsIterator<'a>>>
ServiceHandler for F
{
fn handle<'a>(
&'a self,
service_name: &NamedNode,
graph_pattern: &'a GraphPattern,
) -> Result<BindingsIterator<'a>> {
) -> Result<QuerySolutionsIterator<'a>> {
self(service_name, graph_pattern)
}
}
@ -179,7 +182,11 @@ impl<F: for<'a> Fn(&NamedNode, &'a GraphPattern) -> Result<BindingsIterator<'a>>
struct 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"))
}
}

@ -9,11 +9,12 @@ use rio_turtle::{NTriplesFormatter, TurtleFormatter};
use rio_xml::RdfXmlFormatter;
use std::fmt;
use std::io::{BufRead, Write};
use std::rc::Rc;
/// Results of a [SPARQL query](https://www.w3.org/TR/sparql11-query/)
pub enum QueryResult<'a> {
/// 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
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
@ -119,24 +120,55 @@ impl FileSyntax for QueryResultSyntax {
}
}
/// An iterator over results bindings
pub struct BindingsIterator<'a> {
variables: Vec<Variable>,
/// An iterator over query result solutions
///
/// ```
/// 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>,
}
impl<'a> BindingsIterator<'a> {
impl<'a> QuerySolutionsIterator<'a> {
pub fn new(
variables: Vec<Variable>,
iter: Box<dyn Iterator<Item = Result<Vec<Option<Term>>>> + 'a>,
) -> 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] {
&*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> {
self.iter
}
@ -147,11 +179,108 @@ impl<'a> BindingsIterator<'a> {
Vec<Variable>,
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
///
/// ```
/// use oxigraph::sparql::Variable;
///
/// assert_eq!(
/// "?foo",
/// Variable::new("foo").to_string()
/// )
/// ```
#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Hash)]
pub struct Variable {
name: String,
@ -166,7 +295,7 @@ impl Variable {
&self.name
}
#[deprecated]
#[deprecated(note = "Please use as_str instead")]
pub fn name(&self) -> Result<&str> {
Ok(self.as_str())
}

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

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

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

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

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

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

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

Loading…
Cancel
Save