Allows to query the union all of graphs as the default graph

Introduces QueryOptions
pull/10/head
Tpt 5 years ago
parent f90ad8274d
commit e3fe4c5dc6
  1. 4
      lib/src/lib.rs
  2. 12
      lib/src/repository.rs
  3. 55
      lib/src/sparql/mod.rs
  4. 30
      lib/src/sparql/plan.rs
  5. 4
      lib/src/store/memory.rs
  6. 6
      lib/src/store/mod.rs
  7. 4
      lib/src/store/rocksdb.rs
  8. 4
      lib/tests/sparql_test_cases.rs
  9. 5
      server/src/main.rs

@ -11,7 +11,7 @@
//! ```
//! use rudf::model::*;
//! use rudf::{Repository, RepositoryConnection, MemoryRepository, Result};
//! use crate::rudf::sparql::PreparedQuery;
//! use crate::rudf::sparql::{PreparedQuery, QueryOptions};
//! use rudf::sparql::QueryResult;
//!
//! let repository = MemoryRepository::default();
@ -27,7 +27,7 @@
//! assert_eq!(vec![quad], results.unwrap());
//!
//! // SPARQL query
//! let prepared_query = connection.prepare_query("SELECT ?s WHERE { ?s ?p ?o }", None).unwrap();
//! let prepared_query = connection.prepare_query("SELECT ?s WHERE { ?s ?p ?o }", QueryOptions::default()).unwrap();
//! let results = prepared_query.exec().unwrap();
//! if let QueryResult::Bindings(results) = results {
//! assert_eq!(results.into_values_iter().next().unwrap().unwrap()[0], Some(ex.into()));

@ -1,5 +1,5 @@
use crate::model::*;
use crate::sparql::PreparedQuery;
use crate::sparql::{PreparedQuery, QueryOptions};
use crate::{DatasetSyntax, GraphSyntax, Result};
use std::io::BufRead;
@ -14,7 +14,7 @@ use std::io::BufRead;
/// ```
/// use rudf::model::*;
/// use rudf::{Repository, RepositoryConnection, MemoryRepository, Result};
/// use crate::rudf::sparql::PreparedQuery;
/// use crate::rudf::sparql::{PreparedQuery, QueryOptions};
/// use rudf::sparql::QueryResult;
///
/// let repository = MemoryRepository::default();
@ -30,7 +30,7 @@ use std::io::BufRead;
/// assert_eq!(vec![quad], results.unwrap());
///
/// // SPARQL query
/// let prepared_query = connection.prepare_query("SELECT ?s WHERE { ?s ?p ?o }", None).unwrap();
/// let prepared_query = connection.prepare_query("SELECT ?s WHERE { ?s ?p ?o }", QueryOptions::default()).unwrap();
/// let results = prepared_query.exec().unwrap();
/// if let QueryResult::Bindings(results) = results {
/// assert_eq!(results.into_values_iter().next().unwrap().unwrap()[0], Some(ex.into()));
@ -64,7 +64,7 @@ pub trait RepositoryConnection: Clone {
/// ```
/// use rudf::model::*;
/// use rudf::{Repository, RepositoryConnection, MemoryRepository};
/// use rudf::sparql::PreparedQuery;
/// use rudf::sparql::{PreparedQuery, QueryOptions};
/// use rudf::sparql::QueryResult;
///
/// let repository = MemoryRepository::default();
@ -75,13 +75,13 @@ pub trait RepositoryConnection: Clone {
/// connection.insert(&Quad::new(ex.clone(), ex.clone(), ex.clone(), None));
///
/// // SPARQL query
/// let prepared_query = connection.prepare_query("SELECT ?s WHERE { ?s ?p ?o }", None).unwrap();
/// let prepared_query = connection.prepare_query("SELECT ?s WHERE { ?s ?p ?o }", QueryOptions::default()).unwrap();
/// let results = prepared_query.exec().unwrap();
/// if let QueryResult::Bindings(results) = results {
/// assert_eq!(results.into_values_iter().next().unwrap().unwrap()[0], Some(ex.into()));
/// }
/// ```
fn prepare_query(&self, query: &str, base_iri: Option<&str>) -> Result<Self::PreparedQuery>;
fn prepare_query(&self, query: &str, options: QueryOptions) -> Result<Self::PreparedQuery>;
/// Retrieves quads with a filter on each quad component
///

@ -31,9 +31,9 @@ pub trait PreparedQuery {
}
/// An implementation of `PreparedQuery` for internal use
pub struct SimplePreparedQuery<S: StoreConnection>(SimplePreparedQueryOptions<S>);
pub struct SimplePreparedQuery<S: StoreConnection>(SimplePreparedQueryAction<S>);
enum SimplePreparedQueryOptions<S: StoreConnection> {
enum SimplePreparedQueryAction<S: StoreConnection> {
Select {
plan: PlanNode,
variables: Vec<Variable>,
@ -55,17 +55,17 @@ enum SimplePreparedQueryOptions<S: StoreConnection> {
}
impl<S: StoreConnection> SimplePreparedQuery<S> {
pub(crate) fn new(connection: S, query: &str, base_iri: Option<&str>) -> Result<Self> {
let dataset = DatasetView::new(connection);
pub(crate) fn new(connection: S, query: &str, options: QueryOptions) -> Result<Self> {
let dataset = DatasetView::new(connection, options.default_graph_as_union);
//TODO avoid inserting terms in the Repository StringStore
Ok(Self(match read_sparql_query(query, base_iri)? {
Ok(Self(match read_sparql_query(query, options.base_iri)? {
QueryVariants::Select {
algebra,
dataset: _,
base_iri,
} => {
let (plan, variables) = PlanBuilder::build(dataset.encoder(), &algebra)?;
SimplePreparedQueryOptions::Select {
SimplePreparedQueryAction::Select {
plan,
variables,
evaluator: SimpleEvaluator::new(dataset, base_iri),
@ -77,7 +77,7 @@ impl<S: StoreConnection> SimplePreparedQuery<S> {
base_iri,
} => {
let (plan, _) = PlanBuilder::build(dataset.encoder(), &algebra)?;
SimplePreparedQueryOptions::Ask {
SimplePreparedQueryAction::Ask {
plan,
evaluator: SimpleEvaluator::new(dataset, base_iri),
}
@ -89,7 +89,7 @@ impl<S: StoreConnection> SimplePreparedQuery<S> {
base_iri,
} => {
let (plan, variables) = PlanBuilder::build(dataset.encoder(), &algebra)?;
SimplePreparedQueryOptions::Construct {
SimplePreparedQueryAction::Construct {
plan,
construct: PlanBuilder::build_graph_template(
dataset.encoder(),
@ -105,7 +105,7 @@ impl<S: StoreConnection> SimplePreparedQuery<S> {
base_iri,
} => {
let (plan, _) = PlanBuilder::build(dataset.encoder(), &algebra)?;
SimplePreparedQueryOptions::Describe {
SimplePreparedQueryAction::Describe {
plan,
evaluator: SimpleEvaluator::new(dataset, base_iri),
}
@ -117,26 +117,55 @@ impl<S: StoreConnection> SimplePreparedQuery<S> {
impl<S: StoreConnection> PreparedQuery for SimplePreparedQuery<S> {
fn exec(&self) -> Result<QueryResult<'_>> {
match &self.0 {
SimplePreparedQueryOptions::Select {
SimplePreparedQueryAction::Select {
plan,
variables,
evaluator,
} => evaluator.evaluate_select_plan(&plan, &variables),
SimplePreparedQueryOptions::Ask { plan, evaluator } => {
SimplePreparedQueryAction::Ask { plan, evaluator } => {
evaluator.evaluate_ask_plan(&plan)
}
SimplePreparedQueryOptions::Construct {
SimplePreparedQueryAction::Construct {
plan,
construct,
evaluator,
} => evaluator.evaluate_construct_plan(&plan, &construct),
SimplePreparedQueryOptions::Describe { plan, evaluator } => {
SimplePreparedQueryAction::Describe { plan, evaluator } => {
evaluator.evaluate_describe_plan(&plan)
}
}
}
}
/// 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,
}
impl<'a> Default for QueryOptions<'a> {
fn default() -> Self {
Self {
base_iri: None,
default_graph_as_union: false,
}
}
}
impl<'a> QueryOptions<'a> {
/// Allows to set the base IRI of the query
pub fn with_base_iri(mut self, base_iri: &'a str) -> Self {
self.base_iri = Some(base_iri);
self
}
/// Consider the union of all graphs in the repository as the default graph
pub fn with_default_graph_as_union(mut self) -> Self {
self.default_graph_as_union = true;
self
}
}
/// A parsed [SPARQL query](https://www.w3.org/TR/sparql11-query/)
#[derive(Eq, PartialEq, Debug, Clone, Hash)]
pub struct Query(QueryVariants);

@ -1,6 +1,7 @@
use crate::sparql::eval::StringOrStoreString;
use crate::store::numeric_encoder::{
EncodedQuad, EncodedTerm, Encoder, MemoryStrStore, StrContainer, StrLookup,
ENCODED_DEFAULT_GRAPH,
};
use crate::store::StoreConnection;
use crate::Result;
@ -462,13 +463,15 @@ pub enum TripleTemplateValue {
pub struct DatasetView<S: StoreConnection> {
store: S,
extra: RefCell<MemoryStrStore>,
default_graph_as_union: bool,
}
impl<S: StoreConnection> DatasetView<S> {
pub fn new(store: S) -> Self {
pub fn new(store: S, default_graph_as_union: bool) -> Self {
Self {
store,
extra: RefCell::new(MemoryStrStore::default()),
default_graph_as_union,
}
}
@ -479,9 +482,34 @@ impl<S: StoreConnection> DatasetView<S> {
object: Option<EncodedTerm>,
graph_name: Option<EncodedTerm>,
) -> Box<dyn Iterator<Item = Result<EncodedQuad>> + 'a> {
if graph_name == None {
Box::new(
self.store
.quads_for_pattern(subject, predicate, object, None)
.filter(|quad| match quad {
Err(_) => true,
Ok(quad) => quad.graph_name != ENCODED_DEFAULT_GRAPH,
}),
)
} else if graph_name == Some(ENCODED_DEFAULT_GRAPH) && self.default_graph_as_union {
Box::new(
self.store
.quads_for_pattern(subject, predicate, object, None)
.map(|quad| {
let quad = quad?;
Ok(EncodedQuad::new(
quad.subject,
quad.predicate,
quad.object,
ENCODED_DEFAULT_GRAPH,
))
}),
)
} else {
self.store
.quads_for_pattern(subject, predicate, object, graph_name)
}
}
pub fn encoder<'a>(&'a self) -> impl Encoder + StrContainer + 'a {
DatasetViewStrContainer {

@ -14,7 +14,7 @@ use std::sync::{PoisonError, RwLock, RwLockReadGuard, RwLockWriteGuard};
/// use rudf::model::*;
/// use rudf::{Repository, RepositoryConnection, MemoryRepository, Result};
/// use crate::rudf::sparql::PreparedQuery;
/// use rudf::sparql::QueryResult;
/// use rudf::sparql::{QueryResult, QueryOptions};
///
/// let repository = MemoryRepository::default();
/// let mut connection = repository.connection().unwrap();
@ -29,7 +29,7 @@ use std::sync::{PoisonError, RwLock, RwLockReadGuard, RwLockWriteGuard};
/// assert_eq!(vec![quad], results.unwrap());
///
/// // SPARQL query
/// let prepared_query = connection.prepare_query("SELECT ?s WHERE { ?s ?p ?o }", None).unwrap();
/// let prepared_query = connection.prepare_query("SELECT ?s WHERE { ?s ?p ?o }", QueryOptions::default()).unwrap();
/// let results = prepared_query.exec().unwrap();
/// if let QueryResult::Bindings(results) = results {
/// assert_eq!(results.into_values_iter().next().unwrap().unwrap()[0], Some(ex.into()));

@ -10,7 +10,7 @@ pub use crate::store::memory::MemoryRepository;
pub use crate::store::rocksdb::RocksDbRepository;
use crate::model::*;
use crate::sparql::SimplePreparedQuery;
use crate::sparql::{QueryOptions, SimplePreparedQuery};
use crate::store::numeric_encoder::*;
use crate::{DatasetSyntax, GraphSyntax, RepositoryConnection, Result};
use rio_api::parser::{QuadsParser, TriplesParser};
@ -71,8 +71,8 @@ impl<S: StoreConnection> From<S> for StoreRepositoryConnection<S> {
impl<S: StoreConnection> RepositoryConnection for StoreRepositoryConnection<S> {
type PreparedQuery = SimplePreparedQuery<S>;
fn prepare_query(&self, query: &str, base_iri: Option<&str>) -> Result<SimplePreparedQuery<S>> {
SimplePreparedQuery::new(self.inner.clone(), query, base_iri) //TODO: avoid clone
fn prepare_query(&self, query: &str, options: QueryOptions) -> Result<SimplePreparedQuery<S>> {
SimplePreparedQuery::new(self.inner.clone(), query, options) //TODO: avoid clone
}
fn quads_for_pattern<'a>(

@ -24,7 +24,7 @@ use std::str;
/// ```ignored
/// use rudf::model::*;
/// use rudf::{Repository, RepositoryConnection, RocksDbRepository, Result};
/// use crate::rudf::sparql::PreparedQuery;
/// use crate::rudf::sparql::{PreparedQuery, QueryOptions};
/// use rudf::sparql::QueryResult;
///
/// let repository = RocksDbRepository::open("example.db").unwrap();
@ -40,7 +40,7 @@ use std::str;
/// assert_eq!(vec![quad], results.unwrap());
///
/// // SPARQL query
/// let prepared_query = connection.prepare_query("SELECT ?s WHERE { ?s ?p ?o }", None).unwrap();
/// let prepared_query = connection.prepare_query("SELECT ?s WHERE { ?s ?p ?o }", QueryOptions::default()).unwrap();
/// let results = prepared_query.exec().unwrap();
/// if let QueryResult::Bindings(results) = results {
/// assert_eq!(results.into_values_iter().next().unwrap().unwrap()[0], Some(ex.into()));

@ -4,7 +4,7 @@ use rayon::prelude::*;
use rudf::model::vocab::rdf;
use rudf::model::vocab::rdfs;
use rudf::model::*;
use rudf::sparql::PreparedQuery;
use rudf::sparql::{PreparedQuery, QueryOptions};
use rudf::sparql::{Query, QueryResult, QueryResultSyntax};
use rudf::{GraphSyntax, MemoryRepository, Repository, RepositoryConnection, Result};
use std::fmt;
@ -158,7 +158,7 @@ fn sparql_w3c_query_evaluation_testsuite() -> Result<()> {
}
match repository
.connection()?
.prepare_query(&read_file_to_string(&test.query)?, Some(&test.query))
.prepare_query(&read_file_to_string(&test.query)?, QueryOptions::default().with_base_iri(&test.query))
{
Err(error) => Err(format_err!(
"Failure to parse query of {} with error: {}",

@ -4,8 +4,7 @@ use clap::ArgMatches;
use rouille::input::priority_header_preferred;
use rouille::url::form_urlencoded;
use rouille::{content_encoding, start_server, Request, Response};
use rudf::sparql::QueryResult;
use rudf::sparql::{PreparedQuery, QueryResultSyntax};
use rudf::sparql::{PreparedQuery, QueryOptions, QueryResult, QueryResultSyntax};
use rudf::{
DatasetSyntax, FileSyntax, GraphSyntax, MemoryRepository, Repository, RepositoryConnection,
RocksDbRepository,
@ -149,7 +148,7 @@ fn evaluate_sparql_query<R: RepositoryConnection>(
request: &Request,
) -> Response {
//TODO: stream
match connection.prepare_query(query, None) {
match connection.prepare_query(query, QueryOptions::default()) {
Ok(query) => {
let results = query.exec().unwrap();
if let QueryResult::Graph(_) = results {

Loading…
Cancel
Save