@ -21,7 +21,7 @@
//! if let QueryResults::Solutions(mut solutions) = store.query("SELECT ?s WHERE { ?s ?p ?o }")? {
//! if let QueryResults::Solutions(mut solutions) = store.query("SELECT ?s WHERE { ?s ?p ?o }")? {
//! assert_eq!(solutions.next().unwrap()?.get("s"), Some(&ex.into()));
//! assert_eq!(solutions.next().unwrap()?.get("s"), Some(&ex.into()));
//! };
//! };
//! # Result::<_,Box<dyn std::error::Error>>::Ok(())
//! # Result::<_, Box<dyn std::error::Error>>::Ok(())
//! ```
//! ```
use crate ::io ::read ::ParseError ;
use crate ::io ::read ::ParseError ;
use crate ::io ::{
use crate ::io ::{
@ -35,8 +35,11 @@ use crate::sparql::{
#[ cfg(not(target_arch = " wasm32 " )) ]
#[ cfg(not(target_arch = " wasm32 " )) ]
use crate ::storage ::bulk_load ;
use crate ::storage ::bulk_load ;
use crate ::storage ::numeric_encoder ::{ Decoder , EncodedQuad , EncodedTerm } ;
use crate ::storage ::numeric_encoder ::{ Decoder , EncodedQuad , EncodedTerm } ;
use crate ::storage ::{ ChainedDecodingQuadIterator , DecodingGraphIterator , Storage , StorageReader } ;
use crate ::storage ::{
ChainedDecodingQuadIterator , DecodingGraphIterator , Storage , StorageReader , StorageWriter ,
} ;
pub use crate ::storage ::{ CorruptionError , LoaderError , SerializerError , StorageError } ;
pub use crate ::storage ::{ CorruptionError , LoaderError , SerializerError , StorageError } ;
use std ::error ::Error ;
use std ::io ::{ BufRead , Write } ;
use std ::io ::{ BufRead , Write } ;
#[ cfg(not(target_arch = " wasm32 " )) ]
#[ cfg(not(target_arch = " wasm32 " )) ]
use std ::path ::Path ;
use std ::path ::Path ;
@ -76,15 +79,13 @@ use std::{fmt, str};
/// #
/// #
/// # };
/// # };
/// # remove_dir_all("example.db")?;
/// # remove_dir_all("example.db")?;
/// # Result::<_,Box<dyn std::error::Error>>::Ok(())
/// # Result::<_, Box<dyn std::error::Error>>::Ok(())
/// ```
/// ```
#[ derive(Clone) ]
#[ derive(Clone) ]
pub struct Store {
pub struct Store {
storage : Storage ,
storage : Storage ,
}
}
//TODO: indexes for the default graph and indexes for the named graphs (no more Optional and space saving)
impl Store {
impl Store {
/// Creates a temporary [`Store`] that will be deleted after drop.
/// Creates a temporary [`Store`] that will be deleted after drop.
pub fn new ( ) -> Result < Self , StorageError > {
pub fn new ( ) -> Result < Self , StorageError > {
@ -119,7 +120,7 @@ impl Store {
/// if let QueryResults::Solutions(mut solutions) = store.query("SELECT ?s WHERE { ?s ?p ?o }")? {
/// if let QueryResults::Solutions(mut solutions) = store.query("SELECT ?s WHERE { ?s ?p ?o }")? {
/// assert_eq!(solutions.next().unwrap()?.get("s"), Some(&ex.into_owned().into()));
/// assert_eq!(solutions.next().unwrap()?.get("s"), Some(&ex.into_owned().into()));
/// }
/// }
/// # Result::<_,Box<dyn std::error::Error>>::Ok(())
/// # Result::<_, Box<dyn std::error::Error>>::Ok(())
/// ```
/// ```
pub fn query (
pub fn query (
& self ,
& self ,
@ -130,7 +131,6 @@ impl Store {
/// Executes a [SPARQL 1.1 query](https://www.w3.org/TR/sparql11-query/) with some options.
/// Executes a [SPARQL 1.1 query](https://www.w3.org/TR/sparql11-query/) with some options.
///
///
///
/// Usage example with a custom function serializing terms to N-Triples:
/// Usage example with a custom function serializing terms to N-Triples:
/// ```
/// ```
/// use oxigraph::store::Store;
/// use oxigraph::store::Store;
@ -147,14 +147,14 @@ impl Store {
/// )? {
/// )? {
/// assert_eq!(solutions.next().unwrap()?.get("nt"), Some(&Literal::from("\"1\"^^<http://www.w3.org/2001/XMLSchema#integer>").into()));
/// assert_eq!(solutions.next().unwrap()?.get("nt"), Some(&Literal::from("\"1\"^^<http://www.w3.org/2001/XMLSchema#integer>").into()));
/// }
/// }
/// # Result::<_,Box<dyn std::error::Error>>::Ok(())
/// # Result::<_, Box<dyn std::error::Error>>::Ok(())
/// ```
/// ```
pub fn query_opt (
pub fn query_opt (
& self ,
& self ,
query : impl TryInto < Query , Error = impl Into < EvaluationError > > ,
query : impl TryInto < Query , Error = impl Into < EvaluationError > > ,
options : QueryOptions ,
options : QueryOptions ,
) -> Result < QueryResults , EvaluationError > {
) -> Result < QueryResults , EvaluationError > {
evaluate_query ( self . storage . clone ( ) , query , options )
evaluate_query ( self . storage . snapshot ( ) , query , options )
}
}
/// Retrieves quads with a filter on each quad component
/// Retrieves quads with a filter on each quad component
@ -174,7 +174,7 @@ impl Store {
/// // quad filter by object
/// // quad filter by object
/// let results = store.quads_for_pattern(None, None, Some((&ex).into()), None).collect::<Result<Vec<_>,_>>()?;
/// let results = store.quads_for_pattern(None, None, Some((&ex).into()), None).collect::<Result<Vec<_>,_>>()?;
/// assert_eq!(vec![quad], results);
/// assert_eq!(vec![quad], results);
/// # Result::<_,Box<dyn std::error::Error>>::Ok(())
/// # Result::<_, Box<dyn std::error::Error>>::Ok(())
/// ```
/// ```
pub fn quads_for_pattern (
pub fn quads_for_pattern (
& self ,
& self ,
@ -196,11 +196,45 @@ impl Store {
}
}
/// Returns all the quads contained in the store.
/// Returns all the quads contained in the store.
///
/// Usage example:
/// ```
/// use oxigraph::store::Store;
/// use oxigraph::model::*;
///
/// let store = Store::new()?;
///
/// // insertion
/// let ex = NamedNode::new("http://example.com")?;
/// let quad = Quad::new(ex.clone(), ex.clone(), ex.clone(), GraphName::DefaultGraph);
/// store.insert(&quad)?;
///
/// // quad filter by object
/// let results = store.iter().collect::<Result<Vec<_>,_>>()?;
/// assert_eq!(vec![quad], results);
/// # Result::<_, Box<dyn std::error::Error>>::Ok(())
/// ```
pub fn iter ( & self ) -> QuadIter {
pub fn iter ( & self ) -> QuadIter {
self . quads_for_pattern ( None , None , None , None )
self . quads_for_pattern ( None , None , None , None )
}
}
/// Checks if this store contains a given quad.
/// Checks if this store contains a given quad.
///
/// Usage example:
/// ```
/// use oxigraph::store::Store;
/// use oxigraph::model::*;
///
/// let ex = NamedNodeRef::new("http://example.com")?;
/// let quad = QuadRef::new(ex, ex, ex, ex);
///
/// let store = Store::new()?;
/// assert!(!store.contains(quad)?);
///
/// store.insert(quad)?;
/// assert!(store.contains(quad)?);
/// # Result::<_, Box<dyn std::error::Error>>::Ok(())
/// ```
pub fn contains < ' a > ( & self , quad : impl Into < QuadRef < ' a > > ) -> Result < bool , StorageError > {
pub fn contains < ' a > ( & self , quad : impl Into < QuadRef < ' a > > ) -> Result < bool , StorageError > {
let quad = EncodedQuad ::from ( quad . into ( ) ) ;
let quad = EncodedQuad ::from ( quad . into ( ) ) ;
self . storage . snapshot ( ) . contains ( & quad )
self . storage . snapshot ( ) . contains ( & quad )
@ -209,15 +243,74 @@ impl Store {
/// Returns the number of quads in the store.
/// Returns the number of quads in the store.
///
///
/// Warning: this function executes a full scan.
/// Warning: this function executes a full scan.
///
/// Usage example:
/// ```
/// use oxigraph::store::Store;
/// use oxigraph::model::*;
///
/// let ex = NamedNodeRef::new("http://example.com")?;
/// let store = Store::new()?;
/// store.insert(QuadRef::new(ex, ex, ex, ex))?;
/// store.insert(QuadRef::new(ex, ex, ex, GraphNameRef::DefaultGraph))?;
/// assert_eq!(2, store.len()?);
/// # Result::<_, Box<dyn std::error::Error>>::Ok(())
/// ```
pub fn len ( & self ) -> Result < usize , StorageError > {
pub fn len ( & self ) -> Result < usize , StorageError > {
self . storage . snapshot ( ) . len ( )
self . storage . snapshot ( ) . len ( )
}
}
/// Returns if the store is empty.
/// Returns if the store is empty.
///
/// Usage example:
/// ```
/// use oxigraph::store::Store;
/// use oxigraph::model::*;
///
/// let store = Store::new()?;
/// assert!(store.is_empty()?);
///
/// let ex = NamedNodeRef::new("http://example.com")?;
/// store.insert(QuadRef::new(ex, ex, ex, ex))?;
/// assert!(!store.is_empty()?);
/// # Result::<_, Box<dyn std::error::Error>>::Ok(())
/// ```
pub fn is_empty ( & self ) -> Result < bool , StorageError > {
pub fn is_empty ( & self ) -> Result < bool , StorageError > {
self . storage . snapshot ( ) . is_empty ( )
self . storage . snapshot ( ) . is_empty ( )
}
}
/// Executes a transaction.
///
/// Transaction ensures the "repeatable read" isolation level: the store only exposes changes that have
/// been "committed" (i.e. no partial writes) and the exposed state does not change for the complete duration
/// of a read operation (e.g. a SPARQL query) or a read/write operation (e.g. a SPARQL update).
///
/// Usage example:
/// ```
/// use oxigraph::store::{StorageError, Store};
/// use oxigraph::model::*;
///
/// let store = Store::new()?;
/// let a = NamedNodeRef::new("http://example.com/a")?;
/// let b = NamedNodeRef::new("http://example.com/b")?;
///
/// // Copy all triples about ex:a to triples about ex:b
/// store.transaction(|mut transaction| {
/// for q in transaction.quads_for_pattern(Some(a.into()), None, None, None) {
/// let q = q?;
/// transaction.insert(QuadRef::new(b, &q.predicate, &q.object, &q.graph_name))?;
/// }
/// Result::<_, StorageError>::Ok(())
/// })?;
/// # Result::<_, Box<dyn std::error::Error>>::Ok(())
/// ```
pub fn transaction < ' a , ' b : ' a , T , E : Error + ' static + From < StorageError > > (
& ' b self ,
f : impl Fn ( Transaction < ' a > ) -> Result < T , E > ,
) -> Result < T , E > {
self . storage . transaction ( | writer | f ( Transaction { writer } ) )
}
/// Executes a [SPARQL 1.1 update](https://www.w3.org/TR/sparql11-update/).
/// Executes a [SPARQL 1.1 update](https://www.w3.org/TR/sparql11-update/).
///
///
/// The store does not track the existence of empty named graphs.
/// The store does not track the existence of empty named graphs.
@ -234,9 +327,9 @@ impl Store {
/// store.update("INSERT DATA { <http://example.com> <http://example.com> <http://example.com> }")?;
/// store.update("INSERT DATA { <http://example.com> <http://example.com> <http://example.com> }")?;
///
///
/// // we inspect the store contents
/// // we inspect the store contents
/// let ex = NamedNodeRef::new("http://example.com").unwrap() ;
/// let ex = NamedNodeRef::new("http://example.com")? ;
/// assert!(store.contains(QuadRef::new(ex, ex, ex, GraphNameRef::DefaultGraph))?);
/// assert!(store.contains(QuadRef::new(ex, ex, ex, GraphNameRef::DefaultGraph))?);
/// # Result::<_,Box<dyn std::error::Error>>::Ok(())
/// # Result::<_, Box<dyn std::error::Error>>::Ok(())
/// ```
/// ```
pub fn update (
pub fn update (
& self ,
& self ,
@ -260,23 +353,22 @@ impl Store {
/// |args| args.get(0).map(|t| Literal::from(t.to_string()).into())
/// |args| args.get(0).map(|t| Literal::from(t.to_string()).into())
/// )
/// )
/// )?;
/// )?;
/// # Result::<_,Box<dyn std::error::Error>>::Ok(())
/// # Result::<_, Box<dyn std::error::Error>>::Ok(())
/// ```
/// ```
pub fn update_opt (
pub fn update_opt (
& self ,
& self ,
update : impl TryInto < Update , Error = impl Into < EvaluationError > > ,
update : impl TryInto < Update , Error = impl Into < EvaluationError > > ,
options : impl Into < UpdateOptions > ,
options : impl Into < UpdateOptions > ,
) -> Result < ( ) , EvaluationError > {
) -> Result < ( ) , EvaluationError > {
evaluate_update (
let update = update . try_into ( ) . map_err ( std ::convert ::Into ::into ) ? ;
& self . storage ,
let options = options . into ( ) ;
update . try_into ( ) . map_err ( std ::convert ::Into ::into ) ? ,
self . storage
options . into ( ) ,
. transaction ( | mut t | evaluate_update ( & mut t , & update , & options ) )
)
}
}
/// Loads a graph file (i.e. triples) into the store.
/// Loads a graph file (i.e. triples) into the store.
///
///
/// This function is atomic and quite slow and memory hungry. To get much better performances you might want to use [`bulk_load_graph`](Store::bulk_load_graph).
/// This function is atomic, quite slow and memory hungry. To get much better performances you might want to use [`bulk_load_graph`](Store::bulk_load_graph).
///
///
/// Usage example:
/// Usage example:
/// ```
/// ```
@ -293,12 +385,8 @@ impl Store {
/// // we inspect the store contents
/// // we inspect the store contents
/// let ex = NamedNodeRef::new("http://example.com")?;
/// let ex = NamedNodeRef::new("http://example.com")?;
/// assert!(store.contains(QuadRef::new(ex, ex, ex, GraphNameRef::DefaultGraph))?);
/// assert!(store.contains(QuadRef::new(ex, ex, ex, GraphNameRef::DefaultGraph))?);
/// # Result::<_,Box<dyn std::error::Error>>::Ok(())
/// # Result::<_, Box<dyn std::error::Error>>::Ok(())
/// ```
/// ```
///
/// Errors related to parameter validation like the base IRI use the [`InvalidInput`](std::io::ErrorKind::InvalidInput) error kind.
/// Errors related to a bad syntax in the loaded file use the [`InvalidData`](std::io::ErrorKind::InvalidData) or [`UnexpectedEof`](std::io::ErrorKind::UnexpectedEof) error kinds.
/// Errors related to data loading into the store use the other error kinds.
pub fn load_graph < ' a > (
pub fn load_graph < ' a > (
& self ,
& self ,
reader : impl BufRead ,
reader : impl BufRead ,
@ -326,7 +414,7 @@ impl Store {
/// Loads a dataset file (i.e. quads) into the store.
/// Loads a dataset file (i.e. quads) into the store.
///
///
/// This function is atomic and quite slow . To get much better performances you might want to [`bulk_load_dataset`](Store::bulk_load_dataset).
/// This function is atomic, quite slow and memory hungry . To get much better performances you might want to [`bulk_load_dataset`](Store::bulk_load_dataset).
///
///
/// Usage example:
/// Usage example:
/// ```
/// ```
@ -343,12 +431,8 @@ impl Store {
/// // we inspect the store contents
/// // we inspect the store contents
/// let ex = NamedNodeRef::new("http://example.com")?;
/// let ex = NamedNodeRef::new("http://example.com")?;
/// assert!(store.contains(QuadRef::new(ex, ex, ex, ex))?);
/// assert!(store.contains(QuadRef::new(ex, ex, ex, ex))?);
/// # Result::<_,Box<dyn std::error::Error>>::Ok(())
/// # Result::<_, Box<dyn std::error::Error>>::Ok(())
/// ```
/// ```
///
/// Errors related to parameter validation like the base IRI use the [`InvalidInput`](std::io::ErrorKind::InvalidInput) error kind.
/// Errors related to a bad syntax in the loaded file use the [`InvalidData`](std::io::ErrorKind::InvalidData) or [`UnexpectedEof`](std::io::ErrorKind::UnexpectedEof) error kinds.
/// Errors related to data loading into the store use the other error kinds.
pub fn load_dataset (
pub fn load_dataset (
& self ,
& self ,
reader : impl BufRead ,
reader : impl BufRead ,
@ -386,24 +470,22 @@ impl Store {
/// store.insert(quad)?;
/// store.insert(quad)?;
///
///
/// assert!(store.contains(quad)?);
/// assert!(store.contains(quad)?);
/// # Result::<_,Box<dyn std::error::Error>>::Ok(())
/// # Result::<_, Box<dyn std::error::Error>>::Ok(())
/// ```
/// ```
pub fn insert < ' a > ( & self , quad : impl Into < QuadRef < ' a > > ) -> Result < bool , StorageError > {
pub fn insert < ' a > ( & self , quad : impl Into < QuadRef < ' a > > ) -> Result < bool , StorageError > {
let quad = quad . into ( ) ;
let quad = quad . into ( ) ;
self . storage . transaction ( move | mut t | t . insert ( quad ) )
self . transaction ( | mut t | t . insert ( quad ) )
}
}
/// Adds atomically a set of quads to this store.
/// Adds atomically a set of quads to this store.
///
///
/// Warning: This operation uses a memory heavy transaction internally, use [`bulk_extend`](Store::bulk_extend) if you plan to add ten of millions of triples.
/// Warning: This operation uses a memory heavy transaction internally, use [`bulk_extend`](Store::bulk_extend) if you plan to add ten of millions of triples.
pub fn extend ( & self , quads : impl IntoIterator < Item = Quad > ) -> Result < ( ) , StorageError > {
pub fn extend (
let quads = quads . into_iter ( ) . collect ::< Vec < _ > > ( ) ;
& self ,
self . storage . transaction ( move | mut t | {
quads : impl IntoIterator < Item = impl Into < Quad > > ,
for quad in & quads {
) -> Result < ( ) , StorageError > {
t . insert ( quad . into ( ) ) ? ;
let quads = quads . into_iter ( ) . map ( | q | q . into ( ) ) . collect ::< Vec < _ > > ( ) ;
}
self . transaction ( move | mut t | t . extend ( & quads ) )
Ok ( ( ) )
} )
}
}
/// Removes a quad from this store.
/// Removes a quad from this store.
@ -423,11 +505,11 @@ impl Store {
/// store.remove(quad)?;
/// store.remove(quad)?;
///
///
/// assert!(!store.contains(quad)?);
/// assert!(!store.contains(quad)?);
/// # Result::<_,Box<dyn std::error::Error>>::Ok(())
/// # Result::<_, Box<dyn std::error::Error>>::Ok(())
/// ```
/// ```
pub fn remove < ' a > ( & self , quad : impl Into < QuadRef < ' a > > ) -> Result < bool , StorageError > {
pub fn remove < ' a > ( & self , quad : impl Into < QuadRef < ' a > > ) -> Result < bool , StorageError > {
let quad = quad . into ( ) ;
let quad = quad . into ( ) ;
self . storage . transaction ( move | mut t | t . remove ( quad ) )
self . transaction ( move | mut t | t . remove ( quad ) )
}
}
/// Dumps a store graph into a file.
/// Dumps a store graph into a file.
@ -491,7 +573,7 @@ impl Store {
Ok ( ( ) )
Ok ( ( ) )
}
}
/// Returns all the store named graphs
/// Returns all the store named graphs.
///
///
/// Usage example:
/// Usage example:
/// ```
/// ```
@ -503,7 +585,7 @@ impl Store {
/// store.insert(QuadRef::new(&ex, &ex, &ex, &ex))?;
/// store.insert(QuadRef::new(&ex, &ex, &ex, &ex))?;
/// store.insert(QuadRef::new(&ex, &ex, &ex, GraphNameRef::DefaultGraph))?;
/// store.insert(QuadRef::new(&ex, &ex, &ex, GraphNameRef::DefaultGraph))?;
/// assert_eq!(vec![NamedOrBlankNode::from(ex)], store.named_graphs().collect::<Result<Vec<_>,_>>()?);
/// assert_eq!(vec![NamedOrBlankNode::from(ex)], store.named_graphs().collect::<Result<Vec<_>,_>>()?);
/// # Result::<_,Box<dyn std::error::Error>>::Ok(())
/// # Result::<_, Box<dyn std::error::Error>>::Ok(())
/// ```
/// ```
pub fn named_graphs ( & self ) -> GraphNameIter {
pub fn named_graphs ( & self ) -> GraphNameIter {
let reader = self . storage . snapshot ( ) ;
let reader = self . storage . snapshot ( ) ;
@ -524,7 +606,7 @@ impl Store {
/// let store = Store::new()?;
/// let store = Store::new()?;
/// store.insert(QuadRef::new(&ex, &ex, &ex, &ex))?;
/// store.insert(QuadRef::new(&ex, &ex, &ex, &ex))?;
/// assert!(store.contains_named_graph(&ex)?);
/// assert!(store.contains_named_graph(&ex)?);
/// # Result::<_,Box<dyn std::error::Error>>::Ok(())
/// # Result::<_, Box<dyn std::error::Error>>::Ok(())
/// ```
/// ```
pub fn contains_named_graph < ' a > (
pub fn contains_named_graph < ' a > (
& self ,
& self ,
@ -548,15 +630,14 @@ impl Store {
/// store.insert_named_graph(ex)?;
/// store.insert_named_graph(ex)?;
///
///
/// assert_eq!(store.named_graphs().collect::<Result<Vec<_>,_>>()?, vec![ex.into_owned().into()]);
/// assert_eq!(store.named_graphs().collect::<Result<Vec<_>,_>>()?, vec![ex.into_owned().into()]);
/// # Result::<_,Box<dyn std::error::Error>>::Ok(())
/// # Result::<_, Box<dyn std::error::Error>>::Ok(())
/// ```
/// ```
pub fn insert_named_graph < ' a > (
pub fn insert_named_graph < ' a > (
& self ,
& self ,
graph_name : impl Into < NamedOrBlankNodeRef < ' a > > ,
graph_name : impl Into < NamedOrBlankNodeRef < ' a > > ,
) -> Result < bool , StorageError > {
) -> Result < bool , StorageError > {
let graph_name = graph_name . into ( ) ;
let graph_name = graph_name . into ( ) ;
self . storage
self . transaction ( | mut t | t . insert_named_graph ( graph_name ) )
. transaction ( move | mut t | t . insert_named_graph ( graph_name ) )
}
}
/// Clears a graph from this store.
/// Clears a graph from this store.
@ -575,15 +656,14 @@ impl Store {
/// store.clear_graph(ex)?;
/// store.clear_graph(ex)?;
/// assert!(store.is_empty()?);
/// assert!(store.is_empty()?);
/// assert_eq!(1, store.named_graphs().count());
/// assert_eq!(1, store.named_graphs().count());
/// # Result::<_,Box<dyn std::error::Error>>::Ok(())
/// # Result::<_, Box<dyn std::error::Error>>::Ok(())
/// ```
/// ```
pub fn clear_graph < ' a > (
pub fn clear_graph < ' a > (
& self ,
& self ,
graph_name : impl Into < GraphNameRef < ' a > > ,
graph_name : impl Into < GraphNameRef < ' a > > ,
) -> Result < ( ) , StorageError > {
) -> Result < ( ) , StorageError > {
let graph_name = graph_name . into ( ) ;
let graph_name = graph_name . into ( ) ;
self . storage
self . transaction ( | mut t | t . clear_graph ( graph_name ) )
. transaction ( move | mut t | t . clear_graph ( graph_name ) )
}
}
/// Removes a graph from this store.
/// Removes a graph from this store.
@ -604,15 +684,14 @@ impl Store {
/// store.remove_named_graph(ex)?;
/// store.remove_named_graph(ex)?;
/// assert!(store.is_empty()?);
/// assert!(store.is_empty()?);
/// assert_eq!(0, store.named_graphs().count());
/// assert_eq!(0, store.named_graphs().count());
/// # Result::<_,Box<dyn std::error::Error>>::Ok(())
/// # Result::<_, Box<dyn std::error::Error>>::Ok(())
/// ```
/// ```
pub fn remove_named_graph < ' a > (
pub fn remove_named_graph < ' a > (
& self ,
& self ,
graph_name : impl Into < NamedOrBlankNodeRef < ' a > > ,
graph_name : impl Into < NamedOrBlankNodeRef < ' a > > ,
) -> Result < bool , StorageError > {
) -> Result < bool , StorageError > {
let graph_name = graph_name . into ( ) ;
let graph_name = graph_name . into ( ) ;
self . storage
self . transaction ( | mut t | t . remove_named_graph ( graph_name ) )
. transaction ( move | mut t | t . remove_named_graph ( graph_name ) )
}
}
/// Clears the store.
/// Clears the store.
@ -630,10 +709,10 @@ impl Store {
///
///
/// store.clear()?;
/// store.clear()?;
/// assert!(store.is_empty()?);
/// assert!(store.is_empty()?);
/// # Result::<_,Box<dyn std::error::Error>>::Ok(())
/// # Result::<_, Box<dyn std::error::Error>>::Ok(())
/// ```
/// ```
pub fn clear ( & self ) -> Result < ( ) , StorageError > {
pub fn clear ( & self ) -> Result < ( ) , StorageError > {
self . storage . transaction ( | mut t | t . clear ( ) )
self . transaction ( | mut t | t . clear ( ) )
}
}
/// Flushes all buffers and ensures that all writes are saved on disk.
/// Flushes all buffers and ensures that all writes are saved on disk.
@ -679,12 +758,8 @@ impl Store {
/// // we inspect the store contents
/// // we inspect the store contents
/// let ex = NamedNodeRef::new("http://example.com")?;
/// let ex = NamedNodeRef::new("http://example.com")?;
/// assert!(store.contains(QuadRef::new(ex, ex, ex, ex))?);
/// assert!(store.contains(QuadRef::new(ex, ex, ex, ex))?);
/// # Result::<_,Box<dyn std::error::Error>>::Ok(())
/// # Result::<_, Box<dyn std::error::Error>>::Ok(())
/// ```
/// ```
///
/// Errors related to parameter validation like the base IRI use the [`InvalidInput`](std::io::ErrorKind::InvalidInput) error kind.
/// Errors related to a bad syntax in the loaded file use the [`InvalidData`](std::io::ErrorKind::InvalidData) or [`UnexpectedEof`](std::io::ErrorKind::UnexpectedEof) error kinds.
/// Errors related to data loading into the store use the other error kinds.
#[ cfg(not(target_arch = " wasm32 " )) ]
#[ cfg(not(target_arch = " wasm32 " )) ]
pub fn bulk_load_dataset (
pub fn bulk_load_dataset (
& self ,
& self ,
@ -726,12 +801,8 @@ impl Store {
/// // we inspect the store contents
/// // we inspect the store contents
/// let ex = NamedNodeRef::new("http://example.com")?;
/// let ex = NamedNodeRef::new("http://example.com")?;
/// assert!(store.contains(QuadRef::new(ex, ex, ex, GraphNameRef::DefaultGraph))?);
/// assert!(store.contains(QuadRef::new(ex, ex, ex, GraphNameRef::DefaultGraph))?);
/// # Result::<_,Box<dyn std::error::Error>>::Ok(())
/// # Result::<_, Box<dyn std::error::Error>>::Ok(())
/// ```
/// ```
///
/// Errors related to parameter validation like the base IRI use the [`InvalidInput`](std::io::ErrorKind::InvalidInput) error kind.
/// Errors related to a bad syntax in the loaded file use the [`InvalidData`](std::io::ErrorKind::InvalidData) or [`UnexpectedEof`](std::io::ErrorKind::UnexpectedEof) error kinds.
/// Errors related to data loading into the store use the other error kinds.
#[ cfg(not(target_arch = " wasm32 " )) ]
#[ cfg(not(target_arch = " wasm32 " )) ]
pub fn bulk_load_graph < ' a > (
pub fn bulk_load_graph < ' a > (
& self ,
& self ,
@ -777,6 +848,441 @@ impl fmt::Display for Store {
}
}
}
}
/// An object to do operations during a transaction.
///
/// See [`Store::transaction`] for a more detailed description.
pub struct Transaction < ' a > {
writer : StorageWriter < ' a > ,
}
impl < ' a > Transaction < ' a > {
/// Executes a [SPARQL 1.1 query](https://www.w3.org/TR/sparql11-query/).
///
/// Usage example:
/// ```
/// use oxigraph::store::Store;
/// use oxigraph::model::*;
/// use oxigraph::sparql::{EvaluationError, QueryResults};
///
/// let store = Store::new()?;
/// store.transaction(|mut transaction| {
/// if let QueryResults::Solutions(solutions) = transaction.query("SELECT ?s WHERE { ?s ?p ?o }")? {
/// for solution in solutions {
/// if let Some(Term::NamedNode(s)) = solution?.get("s") {
/// transaction.insert(QuadRef::new(s, vocab::rdf::TYPE, NamedNodeRef::new_unchecked("http://example.com"), GraphNameRef::DefaultGraph))?;
/// }
/// }
/// }
/// Result::<_, EvaluationError>::Ok(())
/// })?;
/// # Result::<_, EvaluationError>::Ok(())
/// ```
pub fn query (
& self ,
query : impl TryInto < Query , Error = impl Into < EvaluationError > > ,
) -> Result < QueryResults , EvaluationError > {
self . query_opt ( query , QueryOptions ::default ( ) )
}
/// Executes a [SPARQL 1.1 query](https://www.w3.org/TR/sparql11-query/) with some options.
///
/// Usage example with a custom function serializing terms to N-Triples:
/// ```
/// use oxigraph::store::Store;
/// use oxigraph::model::*;
/// use oxigraph::sparql::{EvaluationError, QueryOptions, QueryResults};
///
/// let store = Store::new()?;
/// store.transaction(|mut transaction| {
/// if let QueryResults::Solutions(solutions) = transaction.query_opt(
/// "SELECT ?s (<http://www.w3.org/ns/formats/N-Triples>(?s) AS ?nt) WHERE { ?s ?p ?o }",
/// QueryOptions::default().with_custom_function(
/// NamedNode::new_unchecked("http://www.w3.org/ns/formats/N-Triples"),
/// |args| args.get(0).map(|t| Literal::from(t.to_string()).into())
/// )
/// )? {
/// for solution in solutions {
/// let solution = solution?;
/// if let (Some(Term::NamedNode(s)), Some(nt)) = (solution.get("s"), solution.get("nt")) {
/// transaction.insert(QuadRef::new(s, NamedNodeRef::new_unchecked("http://example.com/n-triples-representation"), nt, GraphNameRef::DefaultGraph))?;
/// }
/// }
/// }
/// Result::<_, EvaluationError>::Ok(())
/// })?;
/// # Result::<_, EvaluationError>::Ok(())
/// ```
pub fn query_opt (
& self ,
query : impl TryInto < Query , Error = impl Into < EvaluationError > > ,
options : QueryOptions ,
) -> Result < QueryResults , EvaluationError > {
evaluate_query ( self . writer . reader ( ) , query , options )
}
/// Retrieves quads with a filter on each quad component.
///
/// Usage example:
/// Usage example:
/// ```
/// use oxigraph::store::{StorageError, Store};
/// use oxigraph::model::*;
///
/// let store = Store::new()?;
/// let a = NamedNodeRef::new("http://example.com/a")?;
/// let b = NamedNodeRef::new("http://example.com/b")?;
///
/// // Copy all triples about ex:a to triples about ex:b
/// store.transaction(|mut transaction| {
/// for q in transaction.quads_for_pattern(Some(a.into()), None, None, None) {
/// let q = q?;
/// transaction.insert(QuadRef::new(b, &q.predicate, &q.object, &q.graph_name))?;
/// }
/// Result::<_, StorageError>::Ok(())
/// })?;
/// # Result::<_, Box<dyn std::error::Error>>::Ok(())
/// ```
pub fn quads_for_pattern (
& self ,
subject : Option < SubjectRef < ' _ > > ,
predicate : Option < NamedNodeRef < ' _ > > ,
object : Option < TermRef < ' _ > > ,
graph_name : Option < GraphNameRef < ' _ > > ,
) -> QuadIter {
let reader = self . writer . reader ( ) ;
QuadIter {
iter : reader . quads_for_pattern (
subject . map ( EncodedTerm ::from ) . as_ref ( ) ,
predicate . map ( EncodedTerm ::from ) . as_ref ( ) ,
object . map ( EncodedTerm ::from ) . as_ref ( ) ,
graph_name . map ( EncodedTerm ::from ) . as_ref ( ) ,
) ,
reader ,
}
}
/// Returns all the quads contained in the store.
pub fn iter ( & self ) -> QuadIter {
self . quads_for_pattern ( None , None , None , None )
}
/// Checks if this store contains a given quad.
pub fn contains < ' b > ( & self , quad : impl Into < QuadRef < ' b > > ) -> Result < bool , StorageError > {
let quad = EncodedQuad ::from ( quad . into ( ) ) ;
self . writer . reader ( ) . contains ( & quad )
}
/// Returns the number of quads in the store.
///
/// Warning: this function executes a full scan.
pub fn len ( & self ) -> Result < usize , StorageError > {
self . writer . reader ( ) . len ( )
}
/// Returns if the store is empty.
pub fn is_empty ( & self ) -> Result < bool , StorageError > {
self . writer . reader ( ) . is_empty ( )
}
/// Executes a [SPARQL 1.1 update](https://www.w3.org/TR/sparql11-update/).
///
/// The store does not track the existence of empty named graphs.
/// This method has no ACID guarantees.
///
/// Usage example:
/// ```
/// use oxigraph::store::Store;
/// use oxigraph::model::*;
/// use oxigraph::sparql::EvaluationError;
///
/// let store = Store::new()?;
/// store.transaction(|mut transaction| {
/// // insertion
/// transaction.update("INSERT DATA { <http://example.com> <http://example.com> <http://example.com> }")?;
///
/// // we inspect the store contents
/// let ex = NamedNodeRef::new_unchecked("http://example.com");
/// assert!(transaction.contains(QuadRef::new(ex, ex, ex, GraphNameRef::DefaultGraph))?);
/// Result::<_, EvaluationError>::Ok(())
/// })?;
/// # Result::<_, EvaluationError>::Ok(())
/// ```
pub fn update (
& mut self ,
update : impl TryInto < Update , Error = impl Into < EvaluationError > > ,
) -> Result < ( ) , EvaluationError > {
self . update_opt ( update , UpdateOptions ::default ( ) )
}
/// Executes a [SPARQL 1.1 update](https://www.w3.org/TR/sparql11-update/) with some options.
pub fn update_opt (
& mut self ,
update : impl TryInto < Update , Error = impl Into < EvaluationError > > ,
options : impl Into < UpdateOptions > ,
) -> Result < ( ) , EvaluationError > {
evaluate_update (
& mut self . writer ,
& update . try_into ( ) . map_err ( std ::convert ::Into ::into ) ? ,
& options . into ( ) ,
)
}
/// Loads a graph file (i.e. triples) into the store.
///
/// Usage example:
/// ```
/// use oxigraph::store::Store;
/// use oxigraph::io::GraphFormat;
/// use oxigraph::model::*;
///
/// let store = Store::new()?;
///
/// // insertion
/// let file = b"<http://example.com> <http://example.com> <http://example.com> .";
/// store.transaction(|mut transaction| {
/// transaction.load_graph(file.as_ref(), GraphFormat::NTriples, &GraphName::DefaultGraph, None)
/// })?;
///
/// // we inspect the store contents
/// let ex = NamedNodeRef::new_unchecked("http://example.com");
/// assert!(store.contains(QuadRef::new(ex, ex, ex, GraphNameRef::DefaultGraph))?);
/// # Result::<_,oxigraph::store::LoaderError>::Ok(())
/// ```
pub fn load_graph < ' b > (
& mut self ,
reader : impl BufRead ,
format : GraphFormat ,
to_graph_name : impl Into < GraphNameRef < ' b > > ,
base_iri : Option < & str > ,
) -> Result < ( ) , LoaderError > {
let mut parser = GraphParser ::from_format ( format ) ;
if let Some ( base_iri ) = base_iri {
parser = parser
. with_base_iri ( base_iri )
. map_err ( | e | ParseError ::invalid_base_iri ( base_iri , e ) ) ? ;
}
let to_graph_name = to_graph_name . into ( ) ;
for triple in parser . read_triples ( reader ) ? {
self . writer
. insert ( triple ? . as_ref ( ) . in_graph ( to_graph_name ) ) ? ;
}
Ok ( ( ) )
}
/// Loads a dataset file (i.e. quads) into the store.
///
/// Usage example:
/// ```
/// use oxigraph::store::Store;
/// use oxigraph::io::DatasetFormat;
/// use oxigraph::model::*;
///
/// let store = Store::new()?;
///
/// // insertion
/// let file = b"<http://example.com> <http://example.com> <http://example.com> <http://example.com> .";
/// store.transaction(|mut transaction| {
/// transaction.load_dataset(file.as_ref(), DatasetFormat::NQuads, None)
/// })?;
///
/// // we inspect the store contents
/// let ex = NamedNodeRef::new_unchecked("http://example.com");
/// assert!(store.contains(QuadRef::new(ex, ex, ex, ex))?);
/// # Result::<_,oxigraph::store::LoaderError>::Ok(())
/// ```
pub fn load_dataset (
& mut self ,
reader : impl BufRead ,
format : DatasetFormat ,
base_iri : Option < & str > ,
) -> Result < ( ) , LoaderError > {
let mut parser = DatasetParser ::from_format ( format ) ;
if let Some ( base_iri ) = base_iri {
parser = parser
. with_base_iri ( base_iri )
. map_err ( | e | ParseError ::invalid_base_iri ( base_iri , e ) ) ? ;
}
for quad in parser . read_quads ( reader ) ? {
self . writer . insert ( quad ? . as_ref ( ) ) ? ;
}
Ok ( ( ) )
}
/// Adds a quad to this store.
///
/// Returns `true` if the quad was not already in the store.
///
/// Usage example:
/// ```
/// use oxigraph::store::Store;
/// use oxigraph::model::*;
///
/// let ex = NamedNodeRef::new_unchecked("http://example.com");
/// let quad = QuadRef::new(ex, ex, ex, GraphNameRef::DefaultGraph);
///
/// let store = Store::new()?;
/// store.transaction(|mut transaction| {
/// transaction.insert(quad)
/// })?;
/// assert!(store.contains(quad)?);
/// # Result::<_,oxigraph::store::StorageError>::Ok(())
/// ```
pub fn insert < ' b > ( & mut self , quad : impl Into < QuadRef < ' b > > ) -> Result < bool , StorageError > {
self . writer . insert ( quad . into ( ) )
}
/// Adds atomically a set of quads to this store.
///
/// Warning: This operation uses a memory heavy transaction internally, use [`bulk_extend`](Store::bulk_extend) if you plan to add ten of millions of triples.
pub fn extend < ' b > (
& mut self ,
quads : impl IntoIterator < Item = impl Into < QuadRef < ' b > > > ,
) -> Result < ( ) , StorageError > {
for quad in quads {
self . writer . insert ( quad . into ( ) ) ? ;
}
Ok ( ( ) )
}
/// Removes a quad from this store.
///
/// Returns `true` if the quad was in the store and has been removed.
///
/// Usage example:
/// ```
/// use oxigraph::store::Store;
/// use oxigraph::model::*;
///
/// let ex = NamedNodeRef::new_unchecked("http://example.com");
/// let quad = QuadRef::new(ex, ex, ex, GraphNameRef::DefaultGraph);
/// let store = Store::new()?;
/// store.transaction(|mut transaction| {
/// transaction.insert(quad)?;
/// transaction.remove(quad)
/// })?;
/// assert!(!store.contains(quad)?);
/// # Result::<_,oxigraph::store::StorageError>::Ok(())
/// ```
pub fn remove < ' b > ( & mut self , quad : impl Into < QuadRef < ' b > > ) -> Result < bool , StorageError > {
self . writer . remove ( quad . into ( ) )
}
/// Returns all the store named graphs.
pub fn named_graphs ( & self ) -> GraphNameIter {
let reader = self . writer . reader ( ) ;
GraphNameIter {
iter : reader . named_graphs ( ) ,
reader ,
}
}
/// Checks if the store contains a given graph.
pub fn contains_named_graph < ' b > (
& self ,
graph_name : impl Into < NamedOrBlankNodeRef < ' b > > ,
) -> Result < bool , StorageError > {
self . writer
. reader ( )
. contains_named_graph ( & EncodedTerm ::from ( graph_name . into ( ) ) )
}
/// Inserts a graph into this store.
///
/// Returns `true` if the graph was not already in the store.
///
/// Usage example:
/// ```
/// use oxigraph::store::Store;
/// use oxigraph::model::NamedNodeRef;
///
/// let ex = NamedNodeRef::new_unchecked("http://example.com");
/// let store = Store::new()?;
/// store.transaction(|mut transaction| {
/// transaction.insert_named_graph(ex)
/// })?;
/// assert_eq!(store.named_graphs().collect::<Result<Vec<_>,_>>()?, vec![ex.into_owned().into()]);
/// # Result::<_,oxigraph::store::StorageError>::Ok(())
/// ```
pub fn insert_named_graph < ' b > (
& mut self ,
graph_name : impl Into < NamedOrBlankNodeRef < ' b > > ,
) -> Result < bool , StorageError > {
self . writer . insert_named_graph ( graph_name . into ( ) )
}
/// Clears a graph from this store.
///
/// Usage example:
/// ```
/// use oxigraph::store::Store;
/// use oxigraph::model::{NamedNodeRef, QuadRef};
///
/// let ex = NamedNodeRef::new_unchecked("http://example.com");
/// let quad = QuadRef::new(ex, ex, ex, ex);
/// let store = Store::new()?;
/// store.transaction(|mut transaction| {
/// transaction.insert(quad)?;
/// transaction.clear_graph(ex)
/// })?;
/// assert!(store.is_empty()?);
/// assert_eq!(1, store.named_graphs().count());
/// # Result::<_,oxigraph::store::StorageError>::Ok(())
/// ```
pub fn clear_graph < ' b > (
& mut self ,
graph_name : impl Into < GraphNameRef < ' b > > ,
) -> Result < ( ) , StorageError > {
self . writer . clear_graph ( graph_name . into ( ) )
}
/// Removes a graph from this store.
///
/// Returns `true` if the graph was in the store and has been removed.
///
/// Usage example:
/// ```
/// use oxigraph::store::Store;
/// use oxigraph::model::{NamedNodeRef, QuadRef};
///
/// let ex = NamedNodeRef::new_unchecked("http://example.com");
/// let quad = QuadRef::new(ex, ex, ex, ex);
/// let store = Store::new()?;
/// store.transaction(|mut transaction| {
/// transaction.insert(quad)?;
/// transaction.remove_named_graph(ex)
/// })?;
/// assert!(store.is_empty()?);
/// assert_eq!(0, store.named_graphs().count());
/// # Result::<_,oxigraph::store::StorageError>::Ok(())
/// ```
pub fn remove_named_graph < ' b > (
& mut self ,
graph_name : impl Into < NamedOrBlankNodeRef < ' b > > ,
) -> Result < bool , StorageError > {
self . writer . remove_named_graph ( graph_name . into ( ) )
}
/// Clears the store.
///
/// Usage example:
/// ```
/// use oxigraph::store::Store;
/// use oxigraph::model::*;
///
/// let ex = NamedNodeRef::new_unchecked("http://example.com");
/// let store = Store::new()?;
/// store.transaction(|mut transaction| {
/// transaction.insert(QuadRef::new(ex, ex, ex, ex))?;
/// transaction.clear()
/// })?;
/// assert!(store.is_empty()?);
/// # Result::<_,oxigraph::store::StorageError>::Ok(())
/// ```
pub fn clear ( & mut self ) -> Result < ( ) , StorageError > {
self . writer . clear ( )
}
}
/// An iterator returning the quads contained in a [`Store`].
/// An iterator returning the quads contained in a [`Store`].
pub struct QuadIter {
pub struct QuadIter {
iter : ChainedDecodingQuadIterator ,
iter : ChainedDecodingQuadIterator ,