Merge pull request #10 from Tpt/default_graph_union

Allows to query the union all of graphs as the default graph
pull/14/head
Thomas Tanon 5 years ago committed by GitHub
commit 8dc68d8fff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      lib/src/lib.rs
  2. 12
      lib/src/repository.rs
  3. 55
      lib/src/sparql/mod.rs
  4. 34
      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::model::*;
//! use rudf::{Repository, RepositoryConnection, MemoryRepository, Result}; //! use rudf::{Repository, RepositoryConnection, MemoryRepository, Result};
//! use crate::rudf::sparql::PreparedQuery; //! use crate::rudf::sparql::{PreparedQuery, QueryOptions};
//! use rudf::sparql::QueryResult; //! use rudf::sparql::QueryResult;
//! //!
//! let repository = MemoryRepository::default(); //! let repository = MemoryRepository::default();
@ -27,7 +27,7 @@
//! assert_eq!(vec![quad], results.unwrap()); //! assert_eq!(vec![quad], results.unwrap());
//! //!
//! // SPARQL query //! // 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(); //! let results = prepared_query.exec().unwrap();
//! if let QueryResult::Bindings(results) = results { //! if let QueryResult::Bindings(results) = results {
//! assert_eq!(results.into_values_iter().next().unwrap().unwrap()[0], Some(ex.into())); //! assert_eq!(results.into_values_iter().next().unwrap().unwrap()[0], Some(ex.into()));

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

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

@ -1,6 +1,7 @@
use crate::sparql::eval::StringOrStoreString; use crate::sparql::eval::StringOrStoreString;
use crate::store::numeric_encoder::{ use crate::store::numeric_encoder::{
EncodedQuad, EncodedTerm, Encoder, MemoryStrStore, StrContainer, StrLookup, EncodedQuad, EncodedTerm, Encoder, MemoryStrStore, StrContainer, StrLookup,
ENCODED_DEFAULT_GRAPH,
}; };
use crate::store::StoreConnection; use crate::store::StoreConnection;
use crate::Result; use crate::Result;
@ -462,13 +463,15 @@ pub enum TripleTemplateValue {
pub struct DatasetView<S: StoreConnection> { pub struct DatasetView<S: StoreConnection> {
store: S, store: S,
extra: RefCell<MemoryStrStore>, extra: RefCell<MemoryStrStore>,
default_graph_as_union: bool,
} }
impl<S: StoreConnection> DatasetView<S> { impl<S: StoreConnection> DatasetView<S> {
pub fn new(store: S) -> Self { pub fn new(store: S, default_graph_as_union: bool) -> Self {
Self { Self {
store, store,
extra: RefCell::new(MemoryStrStore::default()), extra: RefCell::new(MemoryStrStore::default()),
default_graph_as_union,
} }
} }
@ -479,8 +482,33 @@ impl<S: StoreConnection> DatasetView<S> {
object: Option<EncodedTerm>, object: Option<EncodedTerm>,
graph_name: Option<EncodedTerm>, graph_name: Option<EncodedTerm>,
) -> Box<dyn Iterator<Item = Result<EncodedQuad>> + 'a> { ) -> Box<dyn Iterator<Item = Result<EncodedQuad>> + 'a> {
self.store if graph_name == None {
.quads_for_pattern(subject, predicate, object, graph_name) 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 { pub fn encoder<'a>(&'a self) -> impl Encoder + StrContainer + 'a {

@ -14,7 +14,7 @@ use std::sync::{PoisonError, RwLock, RwLockReadGuard, RwLockWriteGuard};
/// use rudf::model::*; /// use rudf::model::*;
/// use rudf::{Repository, RepositoryConnection, MemoryRepository, Result}; /// use rudf::{Repository, RepositoryConnection, MemoryRepository, Result};
/// use crate::rudf::sparql::PreparedQuery; /// use crate::rudf::sparql::PreparedQuery;
/// use rudf::sparql::QueryResult; /// use rudf::sparql::{QueryResult, QueryOptions};
/// ///
/// let repository = MemoryRepository::default(); /// let repository = MemoryRepository::default();
/// let mut connection = repository.connection().unwrap(); /// let mut connection = repository.connection().unwrap();
@ -29,7 +29,7 @@ use std::sync::{PoisonError, RwLock, RwLockReadGuard, RwLockWriteGuard};
/// assert_eq!(vec![quad], results.unwrap()); /// assert_eq!(vec![quad], results.unwrap());
/// ///
/// // SPARQL query /// // 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(); /// let results = prepared_query.exec().unwrap();
/// if let QueryResult::Bindings(results) = results { /// if let QueryResult::Bindings(results) = results {
/// assert_eq!(results.into_values_iter().next().unwrap().unwrap()[0], Some(ex.into())); /// 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; pub use crate::store::rocksdb::RocksDbRepository;
use crate::model::*; use crate::model::*;
use crate::sparql::SimplePreparedQuery; use crate::sparql::{QueryOptions, SimplePreparedQuery};
use crate::store::numeric_encoder::*; use crate::store::numeric_encoder::*;
use crate::{DatasetSyntax, GraphSyntax, RepositoryConnection, Result}; use crate::{DatasetSyntax, GraphSyntax, RepositoryConnection, Result};
use rio_api::parser::{QuadsParser, TriplesParser}; 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> { impl<S: StoreConnection> RepositoryConnection for StoreRepositoryConnection<S> {
type PreparedQuery = SimplePreparedQuery<S>; type PreparedQuery = SimplePreparedQuery<S>;
fn prepare_query(&self, query: &str, base_iri: Option<&str>) -> Result<SimplePreparedQuery<S>> { fn prepare_query(&self, query: &str, options: QueryOptions) -> Result<SimplePreparedQuery<S>> {
SimplePreparedQuery::new(self.inner.clone(), query, base_iri) //TODO: avoid clone SimplePreparedQuery::new(self.inner.clone(), query, options) //TODO: avoid clone
} }
fn quads_for_pattern<'a>( fn quads_for_pattern<'a>(

@ -24,7 +24,7 @@ use std::str;
/// ```ignored /// ```ignored
/// use rudf::model::*; /// use rudf::model::*;
/// use rudf::{Repository, RepositoryConnection, RocksDbRepository, Result}; /// use rudf::{Repository, RepositoryConnection, RocksDbRepository, Result};
/// use crate::rudf::sparql::PreparedQuery; /// use crate::rudf::sparql::{PreparedQuery, QueryOptions};
/// use rudf::sparql::QueryResult; /// use rudf::sparql::QueryResult;
/// ///
/// let repository = RocksDbRepository::open("example.db").unwrap(); /// let repository = RocksDbRepository::open("example.db").unwrap();
@ -40,7 +40,7 @@ use std::str;
/// assert_eq!(vec![quad], results.unwrap()); /// assert_eq!(vec![quad], results.unwrap());
/// ///
/// // SPARQL query /// // 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(); /// let results = prepared_query.exec().unwrap();
/// if let QueryResult::Bindings(results) = results { /// if let QueryResult::Bindings(results) = results {
/// assert_eq!(results.into_values_iter().next().unwrap().unwrap()[0], Some(ex.into())); /// 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::rdf;
use rudf::model::vocab::rdfs; use rudf::model::vocab::rdfs;
use rudf::model::*; use rudf::model::*;
use rudf::sparql::PreparedQuery; use rudf::sparql::{PreparedQuery, QueryOptions};
use rudf::sparql::{Query, QueryResult, QueryResultSyntax}; use rudf::sparql::{Query, QueryResult, QueryResultSyntax};
use rudf::{GraphSyntax, MemoryRepository, Repository, RepositoryConnection, Result}; use rudf::{GraphSyntax, MemoryRepository, Repository, RepositoryConnection, Result};
use std::fmt; use std::fmt;
@ -158,7 +158,7 @@ fn sparql_w3c_query_evaluation_testsuite() -> Result<()> {
} }
match repository match repository
.connection()? .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!( Err(error) => Err(format_err!(
"Failure to parse query of {} with error: {}", "Failure to parse query of {} with error: {}",

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

Loading…
Cancel
Save