Fork of https://github.com/oxigraph/oxigraph.git for the purpose of NextGraph project
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1107 lines
36 KiB
1107 lines
36 KiB
4 years ago
|
//! API to access an on-on disk [RDF dataset](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-dataset).
|
||
|
//!
|
||
|
//! Usage example:
|
||
|
//! ```
|
||
|
//! use oxigraph::store::Store;
|
||
|
//! use oxigraph::sparql::QueryResults;
|
||
|
//! use oxigraph::model::*;
|
||
|
//! # use std::fs::remove_dir_all;
|
||
|
//!
|
||
|
//! # {
|
||
|
//! let store = Store::open("example.db")?;
|
||
|
//!
|
||
|
//! // insertion
|
||
|
//! let ex = NamedNode::new("http://example.com")?;
|
||
|
//! let quad = Quad::new(ex.clone(), ex.clone(), ex.clone(), None);
|
||
|
//! store.insert(&quad)?;
|
||
|
//!
|
||
|
//! // quad filter
|
||
|
//! let results: Result<Vec<Quad>,_> = store.quads_for_pattern(None, None, None, None).collect();
|
||
|
//! assert_eq!(vec![quad], results?);
|
||
|
//!
|
||
|
//! // SPARQL query
|
||
|
//! if let QueryResults::Solutions(mut solutions) = store.query("SELECT ?s WHERE { ?s ?p ?o }")? {
|
||
|
//! assert_eq!(solutions.next().unwrap()?.get("s"), Some(&ex.into()));
|
||
|
//! };
|
||
|
//! #
|
||
|
//! # };
|
||
|
//! # remove_dir_all("example.db")?;
|
||
|
//! # Result::<_,Box<dyn std::error::Error>>::Ok(())
|
||
|
//! ```
|
||
5 years ago
|
|
||
4 years ago
|
use std::convert::TryInto;
|
||
|
use std::io::{BufRead, Write};
|
||
|
use std::iter::{once, Once};
|
||
|
use std::path::Path;
|
||
|
use std::{fmt, io, str};
|
||
|
|
||
4 years ago
|
use crate::io::{DatasetFormat, GraphFormat};
|
||
5 years ago
|
use crate::model::*;
|
||
4 years ago
|
use crate::sparql::{
|
||
4 years ago
|
evaluate_query, evaluate_update, EvaluationError, Query, QueryOptions, QueryResults, Update,
|
||
4 years ago
|
UpdateOptions,
|
||
4 years ago
|
};
|
||
4 years ago
|
use crate::storage::io::{dump_dataset, dump_graph, load_dataset, load_graph};
|
||
|
use crate::storage::numeric_encoder::{
|
||
4 years ago
|
Decoder, EncodedTerm, ReadEncoder, StrContainer, StrEncodingAware, StrHash, StrLookup,
|
||
|
WriteEncoder,
|
||
4 years ago
|
};
|
||
4 years ago
|
pub use crate::storage::ConflictableTransactionError;
|
||
|
pub use crate::storage::TransactionError;
|
||
|
pub use crate::storage::UnabortableTransactionError;
|
||
|
use crate::storage::{
|
||
|
ChainedDecodingQuadIterator, DecodingGraphIterator, Storage, StorageTransaction,
|
||
4 years ago
|
};
|
||
5 years ago
|
|
||
4 years ago
|
/// An on-on disk [RDF dataset](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-dataset).
|
||
|
/// Allows to query and update it using SPARQL.
|
||
|
/// It is based on the [Sled](https://sled.rs/) key-value database.
|
||
5 years ago
|
///
|
||
4 years ago
|
/// Warning: Sled is not stable yet and might break its storage format.
|
||
5 years ago
|
///
|
||
|
/// Usage example:
|
||
|
/// ```
|
||
4 years ago
|
/// use oxigraph::store::Store;
|
||
4 years ago
|
/// use oxigraph::sparql::QueryResults;
|
||
4 years ago
|
/// use oxigraph::model::*;
|
||
5 years ago
|
/// # use std::fs::remove_dir_all;
|
||
|
///
|
||
|
/// # {
|
||
4 years ago
|
/// let store = Store::open("example.db")?;
|
||
5 years ago
|
///
|
||
|
/// // insertion
|
||
4 years ago
|
/// let ex = NamedNode::new("http://example.com")?;
|
||
5 years ago
|
/// let quad = Quad::new(ex.clone(), ex.clone(), ex.clone(), None);
|
||
|
/// store.insert(&quad)?;
|
||
|
///
|
||
|
/// // quad filter
|
||
4 years ago
|
/// let results: Result<Vec<Quad>,_> = store.quads_for_pattern(None, None, None, None).collect();
|
||
5 years ago
|
/// assert_eq!(vec![quad], results?);
|
||
|
///
|
||
|
/// // SPARQL query
|
||
4 years ago
|
/// if let QueryResults::Solutions(mut solutions) = store.query("SELECT ?s WHERE { ?s ?p ?o }")? {
|
||
5 years ago
|
/// assert_eq!(solutions.next().unwrap()?.get("s"), Some(&ex.into()));
|
||
4 years ago
|
/// };
|
||
5 years ago
|
/// #
|
||
4 years ago
|
/// # };
|
||
5 years ago
|
/// # remove_dir_all("example.db")?;
|
||
4 years ago
|
/// # Result::<_,Box<dyn std::error::Error>>::Ok(())
|
||
5 years ago
|
/// ```
|
||
|
#[derive(Clone)]
|
||
4 years ago
|
pub struct Store {
|
||
4 years ago
|
storage: Storage,
|
||
5 years ago
|
}
|
||
|
|
||
|
//TODO: indexes for the default graph and indexes for the named graphs (no more Optional and space saving)
|
||
|
|
||
4 years ago
|
impl Store {
|
||
|
/// Creates a temporary [`Store`]() that will be deleted after drop.
|
||
4 years ago
|
pub fn new() -> Result<Self, io::Error> {
|
||
4 years ago
|
Ok(Self {
|
||
|
storage: Storage::new()?,
|
||
|
})
|
||
5 years ago
|
}
|
||
|
|
||
4 years ago
|
/// Opens a [`Store`]() and creates it if it does not exist yet.
|
||
4 years ago
|
pub fn open(path: impl AsRef<Path>) -> Result<Self, io::Error> {
|
||
4 years ago
|
Ok(Self {
|
||
|
storage: Storage::open(path.as_ref())?,
|
||
4 years ago
|
})
|
||
5 years ago
|
}
|
||
|
|
||
4 years ago
|
/// Executes a [SPARQL 1.1 query](https://www.w3.org/TR/sparql11-query/).
|
||
5 years ago
|
///
|
||
4 years ago
|
/// Usage example:
|
||
|
/// ```
|
||
4 years ago
|
/// use oxigraph::store::Store;
|
||
4 years ago
|
/// use oxigraph::model::*;
|
||
|
/// use oxigraph::sparql::QueryResults;
|
||
|
///
|
||
4 years ago
|
/// let store = Store::new()?;
|
||
4 years ago
|
///
|
||
|
/// // insertions
|
||
|
/// let ex = NamedNodeRef::new("http://example.com")?;
|
||
|
/// store.insert(QuadRef::new(ex, ex, ex, None))?;
|
||
|
///
|
||
|
/// // SPARQL query
|
||
|
/// 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()));
|
||
|
/// }
|
||
|
/// # Result::<_,Box<dyn std::error::Error>>::Ok(())
|
||
|
/// ```
|
||
4 years ago
|
pub fn query(
|
||
4 years ago
|
&self,
|
||
4 years ago
|
query: impl TryInto<Query, Error = impl Into<EvaluationError>>,
|
||
4 years ago
|
) -> 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.
|
||
|
pub fn query_opt(
|
||
|
&self,
|
||
|
query: impl TryInto<Query, Error = impl Into<EvaluationError>>,
|
||
4 years ago
|
options: QueryOptions,
|
||
4 years ago
|
) -> Result<QueryResults, EvaluationError> {
|
||
4 years ago
|
evaluate_query(self.storage.clone(), query, options)
|
||
5 years ago
|
}
|
||
|
|
||
|
/// Retrieves quads with a filter on each quad component
|
||
|
///
|
||
4 years ago
|
/// Usage example:
|
||
|
/// ```
|
||
4 years ago
|
/// use oxigraph::store::Store;
|
||
4 years ago
|
/// use oxigraph::model::*;
|
||
|
///
|
||
4 years ago
|
/// let store = Store::new()?;
|
||
4 years ago
|
///
|
||
|
/// // insertion
|
||
|
/// let ex = NamedNode::new("http://example.com")?;
|
||
|
/// let quad = Quad::new(ex.clone(), ex.clone(), ex.clone(), None);
|
||
|
/// store.insert(&quad)?;
|
||
|
///
|
||
|
/// // quad filter by object
|
||
|
/// let results = store.quads_for_pattern(None, None, Some((&ex).into()), None).collect::<Result<Vec<_>,_>>()?;
|
||
|
/// assert_eq!(vec![quad], results);
|
||
|
/// # Result::<_,Box<dyn std::error::Error>>::Ok(())
|
||
|
/// ```
|
||
5 years ago
|
pub fn quads_for_pattern(
|
||
|
&self,
|
||
4 years ago
|
subject: Option<NamedOrBlankNodeRef<'_>>,
|
||
|
predicate: Option<NamedNodeRef<'_>>,
|
||
|
object: Option<TermRef<'_>>,
|
||
|
graph_name: Option<GraphNameRef<'_>>,
|
||
4 years ago
|
) -> QuadIter {
|
||
|
QuadIter {
|
||
4 years ago
|
inner: match self.get_encoded_quad_pattern(subject, predicate, object, graph_name) {
|
||
4 years ago
|
Ok(Some((subject, predicate, object, graph_name))) => QuadIterInner::Quads {
|
||
4 years ago
|
iter: self
|
||
|
.storage
|
||
|
.quads_for_pattern(subject, predicate, object, graph_name),
|
||
4 years ago
|
store: self.clone(),
|
||
|
},
|
||
|
Ok(None) => QuadIterInner::Empty,
|
||
|
Err(error) => QuadIterInner::Error(once(error)),
|
||
4 years ago
|
},
|
||
|
}
|
||
5 years ago
|
}
|
||
|
|
||
4 years ago
|
fn get_encoded_quad_pattern(
|
||
|
&self,
|
||
|
subject: Option<NamedOrBlankNodeRef<'_>>,
|
||
|
predicate: Option<NamedNodeRef<'_>>,
|
||
|
object: Option<TermRef<'_>>,
|
||
|
graph_name: Option<GraphNameRef<'_>>,
|
||
|
) -> Result<
|
||
|
Option<(
|
||
|
Option<EncodedTerm>,
|
||
|
Option<EncodedTerm>,
|
||
|
Option<EncodedTerm>,
|
||
|
Option<EncodedTerm>,
|
||
|
)>,
|
||
|
io::Error,
|
||
|
> {
|
||
|
Ok(Some((
|
||
|
if let Some(subject) = transpose(
|
||
|
subject
|
||
|
.map(|t| self.storage.get_encoded_named_or_blank_node(t))
|
||
|
.transpose()?,
|
||
|
) {
|
||
|
subject
|
||
|
} else {
|
||
|
return Ok(None);
|
||
|
},
|
||
|
if let Some(predicate) = transpose(
|
||
|
predicate
|
||
|
.map(|t| self.storage.get_encoded_named_node(t))
|
||
|
.transpose()?,
|
||
|
) {
|
||
|
predicate
|
||
|
} else {
|
||
|
return Ok(None);
|
||
|
},
|
||
|
if let Some(object) = transpose(
|
||
|
object
|
||
|
.map(|t| self.storage.get_encoded_term(t))
|
||
|
.transpose()?,
|
||
|
) {
|
||
|
object
|
||
|
} else {
|
||
|
return Ok(None);
|
||
|
},
|
||
|
if let Some(graph_name) = transpose(
|
||
|
graph_name
|
||
|
.map(|t| self.storage.get_encoded_graph_name(t))
|
||
|
.transpose()?,
|
||
|
) {
|
||
|
graph_name
|
||
|
} else {
|
||
|
return Ok(None);
|
||
|
},
|
||
|
)))
|
||
|
}
|
||
|
|
||
4 years ago
|
/// Returns all the quads contained in the store
|
||
4 years ago
|
pub fn iter(&self) -> QuadIter {
|
||
4 years ago
|
self.quads_for_pattern(None, None, None, None)
|
||
|
}
|
||
|
|
||
5 years ago
|
/// Checks if this store contains a given quad
|
||
4 years ago
|
pub fn contains<'a>(&self, quad: impl Into<QuadRef<'a>>) -> Result<bool, io::Error> {
|
||
|
if let Some(quad) = self.get_encoded_quad(quad.into())? {
|
||
4 years ago
|
self.storage.contains(&quad)
|
||
4 years ago
|
} else {
|
||
|
Ok(false)
|
||
|
}
|
||
5 years ago
|
}
|
||
|
|
||
5 years ago
|
/// Returns the number of quads in the store
|
||
4 years ago
|
///
|
||
|
/// Warning: this function executes a full scan
|
||
5 years ago
|
pub fn len(&self) -> usize {
|
||
4 years ago
|
self.storage.len()
|
||
5 years ago
|
}
|
||
|
|
||
|
/// Returns if the store is empty
|
||
|
pub fn is_empty(&self) -> bool {
|
||
4 years ago
|
self.storage.is_empty()
|
||
4 years ago
|
}
|
||
|
|
||
4 years ago
|
/// 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.
|
||
|
///
|
||
4 years ago
|
/// Usage example:
|
||
|
/// ```
|
||
4 years ago
|
/// use oxigraph::store::Store;
|
||
4 years ago
|
/// use oxigraph::model::*;
|
||
|
///
|
||
4 years ago
|
/// let store = Store::new()?;
|
||
4 years ago
|
///
|
||
|
/// // insertion
|
||
|
/// store.update("INSERT DATA { <http://example.com> <http://example.com> <http://example.com> }")?;
|
||
|
///
|
||
|
/// // we inspect the store contents
|
||
|
/// let ex = NamedNodeRef::new("http://example.com").unwrap();
|
||
|
/// assert!(store.contains(QuadRef::new(ex, ex, ex, None))?);
|
||
|
/// # Result::<_,Box<dyn std::error::Error>>::Ok(())
|
||
|
/// ```
|
||
4 years ago
|
pub fn update(
|
||
|
&self,
|
||
|
update: impl TryInto<Update, Error = impl Into<EvaluationError>>,
|
||
4 years ago
|
) -> 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(
|
||
|
&self,
|
||
|
update: impl TryInto<Update, Error = impl Into<EvaluationError>>,
|
||
|
options: UpdateOptions,
|
||
4 years ago
|
) -> Result<(), EvaluationError> {
|
||
|
evaluate_update(
|
||
4 years ago
|
&self.storage,
|
||
4 years ago
|
update.try_into().map_err(|e| e.into())?,
|
||
4 years ago
|
options,
|
||
4 years ago
|
)
|
||
|
}
|
||
|
|
||
4 years ago
|
/// Executes an ACID transaction.
|
||
4 years ago
|
///
|
||
|
/// The transaction is executed if the given closure returns `Ok`.
|
||
4 years ago
|
/// The transaction is rollbacked if the closure returns `Err`.
|
||
4 years ago
|
///
|
||
4 years ago
|
/// Usage example:
|
||
|
/// ```
|
||
4 years ago
|
/// use oxigraph::store::{ConflictableTransactionError, Store};
|
||
4 years ago
|
/// use oxigraph::model::*;
|
||
|
/// use std::convert::Infallible;
|
||
|
///
|
||
4 years ago
|
/// let store = Store::new()?;
|
||
4 years ago
|
///
|
||
4 years ago
|
/// let ex = NamedNodeRef::new("http://example.com")?;
|
||
|
/// let quad = QuadRef::new(ex, ex, ex, ex);
|
||
4 years ago
|
///
|
||
|
/// // transaction
|
||
|
/// store.transaction(|transaction| {
|
||
4 years ago
|
/// transaction.insert(quad)?;
|
||
4 years ago
|
/// Ok(()) as Result<(),ConflictableTransactionError<Infallible>>
|
||
4 years ago
|
/// })?;
|
||
|
///
|
||
4 years ago
|
/// assert!(store.contains(quad)?);
|
||
|
/// assert!(store.contains_named_graph(ex)?);
|
||
4 years ago
|
/// # Result::<_,Box<dyn std::error::Error>>::Ok(())
|
||
|
/// ```
|
||
|
pub fn transaction<T, E>(
|
||
|
&self,
|
||
4 years ago
|
f: impl Fn(Transaction<'_>) -> Result<T, ConflictableTransactionError<E>>,
|
||
|
) -> Result<T, TransactionError<E>> {
|
||
4 years ago
|
self.storage
|
||
4 years ago
|
.transaction(|storage| f(Transaction { storage }))
|
||
5 years ago
|
}
|
||
|
|
||
5 years ago
|
/// Loads a graph file (i.e. triples) into the store
|
||
|
///
|
||
4 years ago
|
/// Warning: This functions saves the triples in a not atomic way.
|
||
|
/// If the parsing fails in the middle of the file only a part of it may be written to the store.
|
||
4 years ago
|
/// It might leave the store in a bad state if a crash happens during a triple insertion.
|
||
4 years ago
|
/// Use a (memory greedy) [transaction](Store::transaction()) if you do not want that.
|
||
5 years ago
|
///
|
||
4 years ago
|
/// Usage example:
|
||
|
/// ```
|
||
4 years ago
|
/// use oxigraph::store::Store;
|
||
4 years ago
|
/// use oxigraph::io::GraphFormat;
|
||
|
/// use oxigraph::model::*;
|
||
|
///
|
||
4 years ago
|
/// let store = Store::new()?;
|
||
4 years ago
|
///
|
||
|
/// // insertion
|
||
|
/// let file = b"<http://example.com> <http://example.com> <http://example.com> .";
|
||
|
/// store.load_graph(file.as_ref(), GraphFormat::NTriples, &GraphName::DefaultGraph, None)?;
|
||
|
///
|
||
|
/// // we inspect the store contents
|
||
|
/// let ex = NamedNodeRef::new("http://example.com")?;
|
||
|
/// assert!(store.contains(QuadRef::new(ex, ex, ex, None))?);
|
||
|
/// # Result::<_,Box<dyn std::error::Error>>::Ok(())
|
||
|
/// ```
|
||
4 years ago
|
///
|
||
4 years ago
|
/// 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.
|
||
4 years ago
|
/// Errors related to data loading into the store use the other error kinds.
|
||
4 years ago
|
pub fn load_graph<'a>(
|
||
5 years ago
|
&self,
|
||
|
reader: impl BufRead,
|
||
4 years ago
|
format: GraphFormat,
|
||
4 years ago
|
to_graph_name: impl Into<GraphNameRef<'a>>,
|
||
5 years ago
|
base_iri: Option<&str>,
|
||
4 years ago
|
) -> Result<(), io::Error> {
|
||
4 years ago
|
load_graph(
|
||
|
&self.storage,
|
||
|
reader,
|
||
|
format,
|
||
|
to_graph_name.into(),
|
||
|
base_iri,
|
||
|
)?;
|
||
4 years ago
|
Ok(())
|
||
5 years ago
|
}
|
||
|
|
||
|
/// Loads a dataset file (i.e. quads) into the store.
|
||
|
///
|
||
4 years ago
|
/// Warning: This functions saves the triples in a not atomic way.
|
||
|
/// If the parsing fails in the middle of the file, only a part of it may be written to the store.
|
||
4 years ago
|
/// It might leave the store in a bad state if a crash happens during a quad insertion.
|
||
4 years ago
|
/// Use a (memory greedy) [transaction](Store::transaction()) if you do not want that.
|
||
5 years ago
|
///
|
||
4 years ago
|
/// Usage example:
|
||
|
/// ```
|
||
4 years ago
|
/// use oxigraph::store::Store;
|
||
4 years ago
|
/// use oxigraph::io::DatasetFormat;
|
||
|
/// use oxigraph::model::*;
|
||
|
///
|
||
4 years ago
|
/// let store = Store::new()?;
|
||
4 years ago
|
///
|
||
|
/// // insertion
|
||
|
/// let file = b"<http://example.com> <http://example.com> <http://example.com> <http://example.com> .";
|
||
|
/// store.load_dataset(file.as_ref(), DatasetFormat::NQuads, None)?;
|
||
|
///
|
||
|
/// // we inspect the store contents
|
||
|
/// let ex = NamedNodeRef::new("http://example.com")?;
|
||
|
/// assert!(store.contains(QuadRef::new(ex, ex, ex, ex))?);
|
||
|
/// # Result::<_,Box<dyn std::error::Error>>::Ok(())
|
||
|
/// ```
|
||
4 years ago
|
///
|
||
4 years ago
|
/// 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.
|
||
4 years ago
|
/// Errors related to data loading into the store use the other error kinds.
|
||
5 years ago
|
pub fn load_dataset(
|
||
|
&self,
|
||
|
reader: impl BufRead,
|
||
4 years ago
|
format: DatasetFormat,
|
||
5 years ago
|
base_iri: Option<&str>,
|
||
4 years ago
|
) -> Result<(), io::Error> {
|
||
4 years ago
|
load_dataset(&self.storage, reader, format, base_iri)?;
|
||
4 years ago
|
Ok(())
|
||
5 years ago
|
}
|
||
|
|
||
|
/// Adds a quad to this store.
|
||
4 years ago
|
///
|
||
4 years ago
|
/// Returns `true` if the quad was not already in the store.
|
||
|
///
|
||
4 years ago
|
/// This method is optimized for performances and is not atomic.
|
||
|
/// It might leave the store in a bad state if a crash happens during the insertion.
|
||
4 years ago
|
/// Use a (memory greedy) [transaction](Store::transaction()) if you do not want that.
|
||
4 years ago
|
pub fn insert<'a>(&self, quad: impl Into<QuadRef<'a>>) -> Result<bool, io::Error> {
|
||
|
let quad = self.encode_quad(quad.into())?;
|
||
|
self.storage.insert(&quad)
|
||
5 years ago
|
}
|
||
|
|
||
|
/// Removes a quad from this store.
|
||
4 years ago
|
///
|
||
4 years ago
|
/// Returns `true` if the quad was in the store and has been removed.
|
||
|
///
|
||
4 years ago
|
/// This method is optimized for performances and is not atomic.
|
||
|
/// It might leave the store in a bad state if a crash happens during the removal.
|
||
4 years ago
|
/// Use a (memory greedy) [transaction](Store::transaction()) if you do not want that.
|
||
4 years ago
|
pub fn remove<'a>(&self, quad: impl Into<QuadRef<'a>>) -> Result<bool, io::Error> {
|
||
4 years ago
|
if let Some(quad) = self.get_encoded_quad(quad.into())? {
|
||
4 years ago
|
self.storage.remove(&quad)
|
||
4 years ago
|
} else {
|
||
4 years ago
|
Ok(false)
|
||
4 years ago
|
}
|
||
5 years ago
|
}
|
||
4 years ago
|
|
||
|
/// Dumps a store graph into a file.
|
||
|
///
|
||
4 years ago
|
/// Usage example:
|
||
|
/// ```
|
||
4 years ago
|
/// use oxigraph::store::Store;
|
||
4 years ago
|
/// use oxigraph::io::GraphFormat;
|
||
|
/// use oxigraph::model::GraphName;
|
||
|
///
|
||
|
/// let file = "<http://example.com> <http://example.com> <http://example.com> .\n".as_bytes();
|
||
|
///
|
||
4 years ago
|
/// let store = Store::new()?;
|
||
4 years ago
|
/// store.load_graph(file, GraphFormat::NTriples, &GraphName::DefaultGraph, None)?;
|
||
|
///
|
||
|
/// let mut buffer = Vec::new();
|
||
|
/// store.dump_graph(&mut buffer, GraphFormat::NTriples, &GraphName::DefaultGraph)?;
|
||
|
/// assert_eq!(file, buffer.as_slice());
|
||
|
/// # std::io::Result::Ok(())
|
||
|
/// ```
|
||
4 years ago
|
pub fn dump_graph<'a>(
|
||
4 years ago
|
&self,
|
||
4 years ago
|
writer: impl Write,
|
||
4 years ago
|
format: GraphFormat,
|
||
4 years ago
|
from_graph_name: impl Into<GraphNameRef<'a>>,
|
||
4 years ago
|
) -> Result<(), io::Error> {
|
||
4 years ago
|
dump_graph(
|
||
4 years ago
|
self.quads_for_pattern(None, None, None, Some(from_graph_name.into()))
|
||
4 years ago
|
.map(|q| Ok(q?.into())),
|
||
|
writer,
|
||
4 years ago
|
format,
|
||
4 years ago
|
)
|
||
|
}
|
||
|
|
||
4 years ago
|
/// Dumps the store into a file.
|
||
4 years ago
|
///
|
||
4 years ago
|
/// ```
|
||
4 years ago
|
/// use oxigraph::store::Store;
|
||
4 years ago
|
/// use oxigraph::io::DatasetFormat;
|
||
|
///
|
||
|
/// let file = "<http://example.com> <http://example.com> <http://example.com> <http://example.com> .\n".as_bytes();
|
||
|
///
|
||
4 years ago
|
/// let store = Store::new()?;
|
||
4 years ago
|
/// store.load_dataset(file, DatasetFormat::NQuads, None)?;
|
||
|
///
|
||
|
/// let mut buffer = Vec::new();
|
||
|
/// store.dump_dataset(&mut buffer, DatasetFormat::NQuads)?;
|
||
|
/// assert_eq!(file, buffer.as_slice());
|
||
|
/// # std::io::Result::Ok(())
|
||
|
/// ```
|
||
4 years ago
|
pub fn dump_dataset(&self, writer: impl Write, format: DatasetFormat) -> Result<(), io::Error> {
|
||
4 years ago
|
dump_dataset(self.iter(), writer, format)
|
||
4 years ago
|
}
|
||
5 years ago
|
|
||
4 years ago
|
/// Returns all the store named graphs
|
||
|
///
|
||
4 years ago
|
/// Usage example:
|
||
|
/// ```
|
||
4 years ago
|
/// use oxigraph::store::Store;
|
||
4 years ago
|
/// use oxigraph::model::{NamedNode, QuadRef, NamedOrBlankNode};
|
||
|
///
|
||
|
/// let ex = NamedNode::new("http://example.com")?;
|
||
4 years ago
|
/// let store = Store::new()?;
|
||
4 years ago
|
/// store.insert(QuadRef::new(&ex, &ex, &ex, &ex))?;
|
||
|
/// store.insert(QuadRef::new(&ex, &ex, &ex, None))?;
|
||
|
/// assert_eq!(vec![NamedOrBlankNode::from(ex)], store.named_graphs().collect::<Result<Vec<_>,_>>()?);
|
||
|
/// # Result::<_,Box<dyn std::error::Error>>::Ok(())
|
||
|
/// ```
|
||
4 years ago
|
pub fn named_graphs(&self) -> GraphNameIter {
|
||
|
GraphNameIter {
|
||
4 years ago
|
iter: self.storage.named_graphs(),
|
||
4 years ago
|
store: self.clone(),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// Checks if the store contains a given graph
|
||
4 years ago
|
///
|
||
4 years ago
|
/// Usage example:
|
||
|
/// ```
|
||
4 years ago
|
/// use oxigraph::store::Store;
|
||
4 years ago
|
/// use oxigraph::model::{NamedNode, QuadRef};
|
||
|
///
|
||
|
/// let ex = NamedNode::new("http://example.com")?;
|
||
4 years ago
|
/// let store = Store::new()?;
|
||
4 years ago
|
/// store.insert(QuadRef::new(&ex, &ex, &ex, &ex))?;
|
||
|
/// assert!(store.contains_named_graph(&ex)?);
|
||
|
/// # Result::<_,Box<dyn std::error::Error>>::Ok(())
|
||
|
/// ```
|
||
4 years ago
|
pub fn contains_named_graph<'a>(
|
||
|
&self,
|
||
|
graph_name: impl Into<NamedOrBlankNodeRef<'a>>,
|
||
|
) -> Result<bool, io::Error> {
|
||
|
if let Some(graph_name) = self.get_encoded_named_or_blank_node(graph_name.into())? {
|
||
4 years ago
|
self.storage.contains_named_graph(graph_name)
|
||
4 years ago
|
} else {
|
||
|
Ok(false)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// Inserts a graph into this store
|
||
|
///
|
||
4 years ago
|
/// Returns `true` if the graph was not already in the store.
|
||
|
///
|
||
4 years ago
|
/// Usage example:
|
||
|
/// ```
|
||
4 years ago
|
/// use oxigraph::store::Store;
|
||
4 years ago
|
/// use oxigraph::model::NamedNodeRef;
|
||
|
///
|
||
|
/// let ex = NamedNodeRef::new("http://example.com")?;
|
||
4 years ago
|
/// let store = Store::new()?;
|
||
4 years ago
|
/// store.insert_named_graph(ex)?;
|
||
|
/// assert_eq!(store.named_graphs().count(), 1);
|
||
|
/// # Result::<_,Box<dyn std::error::Error>>::Ok(())
|
||
|
/// ```
|
||
4 years ago
|
pub fn insert_named_graph<'a>(
|
||
|
&self,
|
||
|
graph_name: impl Into<NamedOrBlankNodeRef<'a>>,
|
||
4 years ago
|
) -> Result<bool, io::Error> {
|
||
|
let graph_name = self.encode_named_or_blank_node(graph_name.into())?;
|
||
|
self.storage.insert_named_graph(graph_name)
|
||
4 years ago
|
}
|
||
|
|
||
|
/// Clears a graph from this store.
|
||
|
///
|
||
4 years ago
|
/// Usage example:
|
||
|
/// ```
|
||
4 years ago
|
/// use oxigraph::store::Store;
|
||
4 years ago
|
/// use oxigraph::model::{NamedNodeRef, QuadRef};
|
||
|
///
|
||
|
/// let ex = NamedNodeRef::new("http://example.com")?;
|
||
|
/// let quad = QuadRef::new(ex, ex, ex, ex);
|
||
4 years ago
|
/// let store = Store::new()?;
|
||
4 years ago
|
/// store.insert(quad)?;
|
||
|
/// assert_eq!(1, store.len());
|
||
|
///
|
||
|
/// store.clear_graph(ex)?;
|
||
|
/// assert_eq!(0, store.len());
|
||
|
/// assert_eq!(1, store.named_graphs().count());
|
||
|
/// # Result::<_,Box<dyn std::error::Error>>::Ok(())
|
||
|
/// ```
|
||
4 years ago
|
pub fn clear_graph<'a>(
|
||
|
&self,
|
||
|
graph_name: impl Into<GraphNameRef<'a>>,
|
||
|
) -> Result<(), io::Error> {
|
||
4 years ago
|
if let Some(graph_name) = self.get_encoded_graph_name(graph_name.into())? {
|
||
4 years ago
|
self.storage.clear_graph(graph_name)
|
||
4 years ago
|
} else {
|
||
|
Ok(())
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// Removes a graph from this store.
|
||
|
///
|
||
4 years ago
|
/// Returns `true` if the graph was in the store and has been removed.
|
||
|
///
|
||
4 years ago
|
/// Usage example:
|
||
|
/// ```
|
||
4 years ago
|
/// use oxigraph::store::Store;
|
||
4 years ago
|
/// use oxigraph::model::{NamedNodeRef, QuadRef};
|
||
|
///
|
||
|
/// let ex = NamedNodeRef::new("http://example.com")?;
|
||
|
/// let quad = QuadRef::new(ex, ex, ex, ex);
|
||
4 years ago
|
/// let store = Store::new()?;
|
||
4 years ago
|
/// store.insert(quad)?;
|
||
|
/// assert_eq!(1, store.len());
|
||
|
///
|
||
|
/// store.remove_named_graph(ex)?;
|
||
|
/// assert!(store.is_empty());
|
||
|
/// assert_eq!(0, store.named_graphs().count());
|
||
|
/// # Result::<_,Box<dyn std::error::Error>>::Ok(())
|
||
|
/// ```
|
||
4 years ago
|
pub fn remove_named_graph<'a>(
|
||
|
&self,
|
||
|
graph_name: impl Into<NamedOrBlankNodeRef<'a>>,
|
||
4 years ago
|
) -> Result<bool, io::Error> {
|
||
4 years ago
|
if let Some(graph_name) = self.get_encoded_named_or_blank_node(graph_name.into())? {
|
||
4 years ago
|
self.storage.remove_named_graph(graph_name)
|
||
4 years ago
|
} else {
|
||
4 years ago
|
Ok(false)
|
||
4 years ago
|
}
|
||
|
}
|
||
|
|
||
4 years ago
|
/// Clears the store.
|
||
|
///
|
||
4 years ago
|
/// Usage example:
|
||
|
/// ```
|
||
4 years ago
|
/// use oxigraph::store::Store;
|
||
4 years ago
|
/// use oxigraph::model::{NamedNodeRef, QuadRef};
|
||
|
///
|
||
|
/// let ex = NamedNodeRef::new("http://example.com")?;
|
||
4 years ago
|
/// let store = Store::new()?;
|
||
4 years ago
|
/// store.insert(QuadRef::new(ex, ex, ex, ex))?;
|
||
|
/// store.insert(QuadRef::new(ex, ex, ex, None))?;
|
||
|
/// assert_eq!(2, store.len());
|
||
|
///
|
||
|
/// store.clear()?;
|
||
|
/// assert!(store.is_empty());
|
||
|
/// # Result::<_,Box<dyn std::error::Error>>::Ok(())
|
||
|
/// ```
|
||
4 years ago
|
pub fn clear(&self) -> Result<(), io::Error> {
|
||
4 years ago
|
self.storage.clear()
|
||
4 years ago
|
}
|
||
5 years ago
|
}
|
||
|
|
||
4 years ago
|
impl fmt::Display for Store {
|
||
5 years ago
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||
4 years ago
|
for t in self.iter() {
|
||
5 years ago
|
writeln!(f, "{}", t.map_err(|_| fmt::Error)?)?;
|
||
|
}
|
||
|
Ok(())
|
||
|
}
|
||
|
}
|
||
|
|
||
4 years ago
|
impl StrEncodingAware for Store {
|
||
4 years ago
|
type Error = io::Error;
|
||
4 years ago
|
}
|
||
4 years ago
|
|
||
4 years ago
|
impl StrLookup for Store {
|
||
4 years ago
|
fn get_str(&self, id: StrHash) -> Result<Option<String>, io::Error> {
|
||
4 years ago
|
self.storage.get_str(id)
|
||
5 years ago
|
}
|
||
4 years ago
|
|
||
|
fn get_str_id(&self, value: &str) -> Result<Option<StrHash>, io::Error> {
|
||
4 years ago
|
self.storage.get_str_id(value)
|
||
4 years ago
|
}
|
||
5 years ago
|
}
|
||
|
|
||
4 years ago
|
impl<'a> StrContainer for &'a Store {
|
||
4 years ago
|
fn insert_str(&self, value: &str) -> Result<StrHash, io::Error> {
|
||
4 years ago
|
let key = StrHash::new(value);
|
||
4 years ago
|
self.storage.insert_str(key, value)?;
|
||
4 years ago
|
Ok(key)
|
||
5 years ago
|
}
|
||
|
}
|
||
|
|
||
4 years ago
|
/// Allows inserting and deleting quads during an ACID transaction with the [`Store`].
|
||
|
pub struct Transaction<'a> {
|
||
4 years ago
|
storage: StorageTransaction<'a>,
|
||
4 years ago
|
}
|
||
|
|
||
4 years ago
|
impl Transaction<'_> {
|
||
4 years ago
|
/// Loads a graph file (i.e. triples) into the store during the transaction.
|
||
|
///
|
||
|
/// Warning: Because the load happens during a transaction,
|
||
|
/// the full file content might be temporarily stored in main memory.
|
||
|
/// Do not use for big files.
|
||
|
///
|
||
4 years ago
|
/// Usage example:
|
||
|
/// ```
|
||
4 years ago
|
/// use oxigraph::store::Store;
|
||
4 years ago
|
/// use oxigraph::io::GraphFormat;
|
||
|
/// use oxigraph::model::*;
|
||
4 years ago
|
/// use oxigraph::store::ConflictableTransactionError;
|
||
4 years ago
|
///
|
||
4 years ago
|
/// let store = Store::new()?;
|
||
4 years ago
|
///
|
||
|
/// // insertion
|
||
|
/// let file = b"<http://example.com> <http://example.com> <http://example.com> .";
|
||
|
/// store.transaction(|transaction| {
|
||
|
/// transaction.load_graph(file.as_ref(), GraphFormat::NTriples, &GraphName::DefaultGraph, None)?;
|
||
4 years ago
|
/// Ok(()) as Result<(),ConflictableTransactionError<std::io::Error>>
|
||
4 years ago
|
/// })?;
|
||
|
///
|
||
|
/// // we inspect the store content
|
||
|
/// let ex = NamedNodeRef::new("http://example.com")?;
|
||
|
/// assert!(store.contains(QuadRef::new(ex, ex, ex, None))?);
|
||
|
/// # Result::<_, Box<dyn std::error::Error>>::Ok(())
|
||
|
/// ```
|
||
4 years ago
|
///
|
||
4 years ago
|
/// If the file parsing fails in the middle of the file, the triples read before are still
|
||
|
/// considered by the transaction. Rollback the transaction by making the transaction closure
|
||
|
/// return an error if you don't want that.
|
||
4 years ago
|
/// Moving up the parsing error through the transaction is enough to do that.
|
||
4 years ago
|
///
|
||
4 years ago
|
/// 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.
|
||
4 years ago
|
pub fn load_graph<'a>(
|
||
4 years ago
|
&self,
|
||
4 years ago
|
reader: impl BufRead,
|
||
4 years ago
|
format: GraphFormat,
|
||
4 years ago
|
to_graph_name: impl Into<GraphNameRef<'a>>,
|
||
4 years ago
|
base_iri: Option<&str>,
|
||
4 years ago
|
) -> Result<(), UnabortableTransactionError> {
|
||
4 years ago
|
load_graph(
|
||
|
&self.storage,
|
||
|
reader,
|
||
|
format,
|
||
|
to_graph_name.into(),
|
||
|
base_iri,
|
||
|
)?;
|
||
4 years ago
|
Ok(())
|
||
4 years ago
|
}
|
||
|
|
||
|
/// Loads a dataset file (i.e. quads) into the store. into the store during the transaction.
|
||
|
///
|
||
|
/// Warning: Because the load happens during a transaction,
|
||
|
/// the full file content might be temporarily stored in main memory.
|
||
|
/// Do not use for big files.
|
||
|
///
|
||
4 years ago
|
/// Usage example:
|
||
|
/// ```
|
||
4 years ago
|
/// use oxigraph::store::{Store, ConflictableTransactionError};
|
||
4 years ago
|
/// use oxigraph::io::DatasetFormat;
|
||
|
/// use oxigraph::model::*;
|
||
4 years ago
|
/// use oxigraph::store::ConflictableTransactionError;
|
||
4 years ago
|
///
|
||
4 years ago
|
/// let store = Store::new()?;
|
||
4 years ago
|
///
|
||
|
/// // insertion
|
||
|
/// let file = b"<http://example.com> <http://example.com> <http://example.com> <http://example.com> .";
|
||
|
/// store.transaction(|transaction| {
|
||
|
/// transaction.load_dataset(file.as_ref(), DatasetFormat::NQuads, None)?;
|
||
4 years ago
|
/// Ok(()) as Result<(),ConflictableTransactionError<std::io::Error>>
|
||
4 years ago
|
/// })?;
|
||
|
///
|
||
|
/// // we inspect the store content
|
||
|
/// let ex = NamedNodeRef::new("http://example.com")?;
|
||
|
/// assert!(store.contains(QuadRef::new(ex, ex, ex, ex))?);
|
||
|
/// # Result::<_, Box<dyn std::error::Error>>::Ok(())
|
||
|
/// ```
|
||
4 years ago
|
///
|
||
|
/// If the file parsing fails in the middle of the file, the quads read before are still
|
||
|
/// considered by the transaction. Rollback the transaction by making the transaction closure
|
||
|
/// return an error if you don't want that.
|
||
4 years ago
|
/// Moving up the parsing error through the transaction is enough to do that.
|
||
4 years ago
|
///
|
||
4 years ago
|
/// 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.
|
||
4 years ago
|
pub fn load_dataset(
|
||
4 years ago
|
&self,
|
||
4 years ago
|
reader: impl BufRead,
|
||
4 years ago
|
format: DatasetFormat,
|
||
4 years ago
|
base_iri: Option<&str>,
|
||
4 years ago
|
) -> Result<(), UnabortableTransactionError> {
|
||
4 years ago
|
Ok(load_dataset(&self.storage, reader, format, base_iri)?)
|
||
4 years ago
|
}
|
||
|
|
||
|
/// Adds a quad to this store during the transaction.
|
||
4 years ago
|
///
|
||
|
/// Returns `true` if the quad was not already in the store.
|
||
4 years ago
|
pub fn insert<'a>(
|
||
|
&self,
|
||
|
quad: impl Into<QuadRef<'a>>,
|
||
4 years ago
|
) -> Result<bool, UnabortableTransactionError> {
|
||
4 years ago
|
let quad = self.encode_quad(quad.into())?;
|
||
|
self.storage.insert(&quad)
|
||
4 years ago
|
}
|
||
|
|
||
|
/// Removes a quad from this store during the transaction.
|
||
4 years ago
|
///
|
||
|
/// Returns `true` if the quad was in the store and has been removed.
|
||
4 years ago
|
pub fn remove<'a>(
|
||
|
&self,
|
||
|
quad: impl Into<QuadRef<'a>>,
|
||
4 years ago
|
) -> Result<bool, UnabortableTransactionError> {
|
||
4 years ago
|
if let Some(quad) = self.get_encoded_quad(quad.into())? {
|
||
|
self.storage.remove(&quad)
|
||
4 years ago
|
} else {
|
||
4 years ago
|
Ok(false)
|
||
4 years ago
|
}
|
||
4 years ago
|
}
|
||
4 years ago
|
|
||
|
/// Inserts a graph into this store during the transaction
|
||
|
///
|
||
|
/// Returns `true` if the graph was not already in the store.
|
||
|
pub fn insert_named_graph<'a>(
|
||
|
&self,
|
||
|
graph_name: impl Into<NamedOrBlankNodeRef<'a>>,
|
||
4 years ago
|
) -> Result<bool, UnabortableTransactionError> {
|
||
4 years ago
|
let graph_name = self.encode_named_or_blank_node(graph_name.into())?;
|
||
|
self.storage.insert_named_graph(graph_name)
|
||
|
}
|
||
4 years ago
|
}
|
||
|
|
||
4 years ago
|
impl<'a> StrEncodingAware for &'a Transaction<'a> {
|
||
|
type Error = UnabortableTransactionError;
|
||
4 years ago
|
}
|
||
|
|
||
4 years ago
|
impl<'a> StrLookup for &'a Transaction<'a> {
|
||
|
fn get_str(&self, id: StrHash) -> Result<Option<String>, UnabortableTransactionError> {
|
||
4 years ago
|
self.storage.get_str(id)
|
||
4 years ago
|
}
|
||
|
|
||
4 years ago
|
fn get_str_id(&self, value: &str) -> Result<Option<StrHash>, UnabortableTransactionError> {
|
||
4 years ago
|
self.storage.get_str_id(value)
|
||
4 years ago
|
}
|
||
4 years ago
|
}
|
||
|
|
||
4 years ago
|
impl<'a> StrContainer for &'a Transaction<'a> {
|
||
|
fn insert_str(&self, value: &str) -> Result<StrHash, UnabortableTransactionError> {
|
||
4 years ago
|
let key = StrHash::new(value);
|
||
4 years ago
|
self.storage.insert_str(key, value)?;
|
||
4 years ago
|
Ok(key)
|
||
4 years ago
|
}
|
||
|
}
|
||
|
|
||
4 years ago
|
/// An iterator returning the quads contained in a [`Store`].
|
||
|
pub struct QuadIter {
|
||
4 years ago
|
inner: QuadIterInner,
|
||
|
}
|
||
|
|
||
|
enum QuadIterInner {
|
||
4 years ago
|
Quads {
|
||
4 years ago
|
iter: ChainedDecodingQuadIterator,
|
||
4 years ago
|
store: Store,
|
||
4 years ago
|
},
|
||
|
Error(Once<io::Error>),
|
||
|
Empty,
|
||
|
}
|
||
|
|
||
4 years ago
|
impl Iterator for QuadIter {
|
||
4 years ago
|
type Item = Result<Quad, io::Error>;
|
||
|
|
||
|
fn next(&mut self) -> Option<Result<Quad, io::Error>> {
|
||
4 years ago
|
match &mut self.inner {
|
||
|
QuadIterInner::Quads { iter, store } => Some(match iter.next()? {
|
||
4 years ago
|
Ok(quad) => store.decode_quad(&quad).map_err(|e| e.into()),
|
||
|
Err(error) => Err(error),
|
||
|
}),
|
||
4 years ago
|
QuadIterInner::Error(iter) => iter.next().map(Err),
|
||
|
QuadIterInner::Empty => None,
|
||
4 years ago
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
4 years ago
|
/// An iterator returning the graph names contained in a [`Store`].
|
||
|
pub struct GraphNameIter {
|
||
4 years ago
|
iter: DecodingGraphIterator,
|
||
4 years ago
|
store: Store,
|
||
4 years ago
|
}
|
||
|
|
||
4 years ago
|
impl Iterator for GraphNameIter {
|
||
4 years ago
|
type Item = Result<NamedOrBlankNode, io::Error>;
|
||
|
|
||
|
fn next(&mut self) -> Option<Result<NamedOrBlankNode, io::Error>> {
|
||
|
Some(
|
||
|
self.iter
|
||
|
.next()?
|
||
|
.and_then(|graph_name| Ok(self.store.decode_named_or_blank_node(graph_name)?)),
|
||
|
)
|
||
|
}
|
||
|
|
||
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
||
|
self.iter.size_hint()
|
||
|
}
|
||
|
}
|
||
|
|
||
4 years ago
|
fn transpose<T>(o: Option<Option<T>>) -> Option<Option<T>> {
|
||
|
match o {
|
||
|
Some(Some(v)) => Some(Some(v)),
|
||
|
Some(None) => None,
|
||
|
None => Some(None),
|
||
|
}
|
||
|
}
|
||
|
|
||
5 years ago
|
#[test]
|
||
4 years ago
|
fn store() -> Result<(), io::Error> {
|
||
5 years ago
|
use crate::model::*;
|
||
|
|
||
|
let main_s = NamedOrBlankNode::from(BlankNode::default());
|
||
4 years ago
|
let main_p = NamedNode::new("http://example.com").unwrap();
|
||
5 years ago
|
let main_o = Term::from(Literal::from(1));
|
||
4 years ago
|
let main_g = GraphName::from(BlankNode::default());
|
||
|
|
||
|
let default_quad = Quad::new(main_s.clone(), main_p.clone(), main_o.clone(), None);
|
||
|
let named_quad = Quad::new(
|
||
|
main_s.clone(),
|
||
|
main_p.clone(),
|
||
|
main_o.clone(),
|
||
|
main_g.clone(),
|
||
|
);
|
||
|
let default_quads = vec![
|
||
|
Quad::new(main_s.clone(), main_p.clone(), Literal::from(0), None),
|
||
|
default_quad.clone(),
|
||
|
Quad::new(
|
||
|
main_s.clone(),
|
||
|
main_p.clone(),
|
||
|
Literal::from(200000000),
|
||
|
None,
|
||
|
),
|
||
|
];
|
||
|
let all_quads = vec![
|
||
5 years ago
|
Quad::new(main_s.clone(), main_p.clone(), Literal::from(0), None),
|
||
4 years ago
|
default_quad.clone(),
|
||
|
Quad::new(
|
||
|
main_s.clone(),
|
||
|
main_p.clone(),
|
||
|
Literal::from(200000000),
|
||
|
None,
|
||
|
),
|
||
|
named_quad.clone(),
|
||
5 years ago
|
];
|
||
|
|
||
4 years ago
|
let store = Store::new()?;
|
||
4 years ago
|
for t in &default_quads {
|
||
4 years ago
|
assert!(store.insert(t)?);
|
||
5 years ago
|
}
|
||
|
|
||
4 years ago
|
let result: Result<_, TransactionError<io::Error>> = store.transaction(|t| {
|
||
4 years ago
|
assert!(t.remove(&default_quad)?);
|
||
|
assert_eq!(t.remove(&default_quad)?, false);
|
||
|
assert!(t.insert(&named_quad)?);
|
||
|
assert_eq!(t.insert(&named_quad)?, false);
|
||
|
assert!(t.insert(&default_quad)?);
|
||
|
assert_eq!(t.insert(&default_quad)?, false);
|
||
4 years ago
|
Ok(())
|
||
|
});
|
||
|
result?;
|
||
4 years ago
|
assert_eq!(store.insert(&default_quad)?, false);
|
||
4 years ago
|
|
||
4 years ago
|
assert_eq!(store.len(), 4);
|
||
4 years ago
|
assert_eq!(store.iter().collect::<Result<Vec<_>, _>>()?, all_quads);
|
||
5 years ago
|
assert_eq!(
|
||
|
store
|
||
4 years ago
|
.quads_for_pattern(Some(main_s.as_ref()), None, None, None)
|
||
4 years ago
|
.collect::<Result<Vec<_>, _>>()?,
|
||
4 years ago
|
all_quads
|
||
5 years ago
|
);
|
||
|
assert_eq!(
|
||
|
store
|
||
4 years ago
|
.quads_for_pattern(Some(main_s.as_ref()), Some(main_p.as_ref()), None, None)
|
||
4 years ago
|
.collect::<Result<Vec<_>, _>>()?,
|
||
4 years ago
|
all_quads
|
||
5 years ago
|
);
|
||
|
assert_eq!(
|
||
|
store
|
||
4 years ago
|
.quads_for_pattern(
|
||
|
Some(main_s.as_ref()),
|
||
|
Some(main_p.as_ref()),
|
||
|
Some(main_o.as_ref()),
|
||
|
None
|
||
|
)
|
||
4 years ago
|
.collect::<Result<Vec<_>, _>>()?,
|
||
4 years ago
|
vec![default_quad.clone(), named_quad.clone()]
|
||
5 years ago
|
);
|
||
|
assert_eq!(
|
||
|
store
|
||
4 years ago
|
.quads_for_pattern(
|
||
4 years ago
|
Some(main_s.as_ref()),
|
||
|
Some(main_p.as_ref()),
|
||
|
Some(main_o.as_ref()),
|
||
|
Some(GraphNameRef::DefaultGraph)
|
||
4 years ago
|
)
|
||
4 years ago
|
.collect::<Result<Vec<_>, _>>()?,
|
||
4 years ago
|
vec![default_quad.clone()]
|
||
|
);
|
||
|
assert_eq!(
|
||
|
store
|
||
|
.quads_for_pattern(
|
||
|
Some(main_s.as_ref()),
|
||
|
Some(main_p.as_ref()),
|
||
|
Some(main_o.as_ref()),
|
||
|
Some(main_g.as_ref())
|
||
|
)
|
||
|
.collect::<Result<Vec<_>, _>>()?,
|
||
|
vec![named_quad.clone()]
|
||
5 years ago
|
);
|
||
|
assert_eq!(
|
||
|
store
|
||
4 years ago
|
.quads_for_pattern(
|
||
4 years ago
|
Some(main_s.as_ref()),
|
||
|
Some(main_p.as_ref()),
|
||
4 years ago
|
None,
|
||
4 years ago
|
Some(GraphNameRef::DefaultGraph)
|
||
4 years ago
|
)
|
||
4 years ago
|
.collect::<Result<Vec<_>, _>>()?,
|
||
4 years ago
|
default_quads
|
||
5 years ago
|
);
|
||
|
assert_eq!(
|
||
|
store
|
||
4 years ago
|
.quads_for_pattern(Some(main_s.as_ref()), None, Some(main_o.as_ref()), None)
|
||
4 years ago
|
.collect::<Result<Vec<_>, _>>()?,
|
||
4 years ago
|
vec![default_quad.clone(), named_quad.clone()]
|
||
5 years ago
|
);
|
||
|
assert_eq!(
|
||
|
store
|
||
4 years ago
|
.quads_for_pattern(
|
||
4 years ago
|
Some(main_s.as_ref()),
|
||
4 years ago
|
None,
|
||
4 years ago
|
Some(main_o.as_ref()),
|
||
|
Some(GraphNameRef::DefaultGraph)
|
||
4 years ago
|
)
|
||
4 years ago
|
.collect::<Result<Vec<_>, _>>()?,
|
||
4 years ago
|
vec![default_quad.clone()]
|
||
|
);
|
||
|
assert_eq!(
|
||
|
store
|
||
|
.quads_for_pattern(
|
||
|
Some(main_s.as_ref()),
|
||
|
None,
|
||
|
Some(main_o.as_ref()),
|
||
|
Some(main_g.as_ref())
|
||
|
)
|
||
|
.collect::<Result<Vec<_>, _>>()?,
|
||
|
vec![named_quad.clone()]
|
||
5 years ago
|
);
|
||
|
assert_eq!(
|
||
|
store
|
||
4 years ago
|
.quads_for_pattern(
|
||
|
Some(main_s.as_ref()),
|
||
|
None,
|
||
|
None,
|
||
|
Some(GraphNameRef::DefaultGraph)
|
||
|
)
|
||
4 years ago
|
.collect::<Result<Vec<_>, _>>()?,
|
||
4 years ago
|
default_quads
|
||
5 years ago
|
);
|
||
|
assert_eq!(
|
||
|
store
|
||
4 years ago
|
.quads_for_pattern(None, Some(main_p.as_ref()), None, None)
|
||
4 years ago
|
.collect::<Result<Vec<_>, _>>()?,
|
||
4 years ago
|
all_quads
|
||
5 years ago
|
);
|
||
|
assert_eq!(
|
||
|
store
|
||
4 years ago
|
.quads_for_pattern(None, Some(main_p.as_ref()), Some(main_o.as_ref()), None)
|
||
4 years ago
|
.collect::<Result<Vec<_>, _>>()?,
|
||
4 years ago
|
vec![default_quad.clone(), named_quad.clone()]
|
||
5 years ago
|
);
|
||
|
assert_eq!(
|
||
|
store
|
||
4 years ago
|
.quads_for_pattern(None, None, Some(main_o.as_ref()), None)
|
||
4 years ago
|
.collect::<Result<Vec<_>, _>>()?,
|
||
4 years ago
|
vec![default_quad.clone(), named_quad.clone()]
|
||
5 years ago
|
);
|
||
|
assert_eq!(
|
||
|
store
|
||
4 years ago
|
.quads_for_pattern(None, None, None, Some(GraphNameRef::DefaultGraph))
|
||
4 years ago
|
.collect::<Result<Vec<_>, _>>()?,
|
||
4 years ago
|
default_quads
|
||
5 years ago
|
);
|
||
|
assert_eq!(
|
||
|
store
|
||
4 years ago
|
.quads_for_pattern(
|
||
|
None,
|
||
4 years ago
|
Some(main_p.as_ref()),
|
||
|
Some(main_o.as_ref()),
|
||
|
Some(GraphNameRef::DefaultGraph)
|
||
4 years ago
|
)
|
||
4 years ago
|
.collect::<Result<Vec<_>, _>>()?,
|
||
4 years ago
|
vec![default_quad]
|
||
|
);
|
||
|
assert_eq!(
|
||
|
store
|
||
|
.quads_for_pattern(
|
||
|
None,
|
||
|
Some(main_p.as_ref()),
|
||
|
Some(main_o.as_ref()),
|
||
|
Some(main_g.as_ref())
|
||
|
)
|
||
|
.collect::<Result<Vec<_>, _>>()?,
|
||
|
vec![named_quad]
|
||
5 years ago
|
);
|
||
|
|
||
|
Ok(())
|
||
|
}
|