parent
ec67f4928a
commit
dc08e181a8
@ -0,0 +1,98 @@ |
||||
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Throughput}; |
||||
use oxigraph::model::*; |
||||
use oxigraph::*; |
||||
use rand::random; |
||||
use std::env::temp_dir; |
||||
use std::fs::remove_dir_all; |
||||
|
||||
criterion_group!( |
||||
store_load, |
||||
memory_load_bench, |
||||
sled_load_bench, |
||||
rocksdb_load_bench |
||||
); |
||||
|
||||
criterion_main!(store_load); |
||||
|
||||
fn memory_load_bench(c: &mut Criterion) { |
||||
let mut group = c.benchmark_group("memory"); |
||||
group.nresamples(10); |
||||
group.sample_size(10); |
||||
for size in [100, 1_000, 10_000].iter() { |
||||
group.throughput(Throughput::Elements(*size as u64)); |
||||
let quads = create_quads(*size); |
||||
group.bench_function(BenchmarkId::from_parameter(size), |b| { |
||||
b.iter(|| { |
||||
let store = MemoryStore::new(); |
||||
for quad in &quads { |
||||
store.insert(quad).unwrap(); |
||||
} |
||||
}); |
||||
}); |
||||
} |
||||
group.finish(); |
||||
} |
||||
|
||||
fn sled_load_bench(c: &mut Criterion) { |
||||
let mut group = c.benchmark_group("sled"); |
||||
group.nresamples(10); |
||||
group.sample_size(10); |
||||
for size in [100, 1_000, 10_000].iter() { |
||||
group.throughput(Throughput::Elements(*size as u64)); |
||||
let quads = create_quads(*size); |
||||
group.bench_function(BenchmarkId::from_parameter(size), |b| { |
||||
b.iter(|| { |
||||
let store = SledStore::new().unwrap(); |
||||
for quad in &quads { |
||||
store.insert(quad).unwrap(); |
||||
} |
||||
}); |
||||
}); |
||||
} |
||||
group.finish(); |
||||
} |
||||
|
||||
fn rocksdb_load_bench(c: &mut Criterion) { |
||||
let mut group = c.benchmark_group("rocksdb"); |
||||
group.nresamples(10); |
||||
group.sample_size(10); |
||||
let temp_dir = temp_dir(); |
||||
for size in [100, 1_000, 10_000].iter() { |
||||
group.throughput(Throughput::Elements(*size as u64)); |
||||
let quads = create_quads(*size); |
||||
group.bench_function(BenchmarkId::from_parameter(size), |b| { |
||||
b.iter(|| { |
||||
let mut dir = temp_dir.clone(); |
||||
dir.push(random::<u64>().to_string()); |
||||
let store = RocksDbStore::open(&dir).unwrap(); |
||||
for quad in &quads { |
||||
store.insert(quad).unwrap(); |
||||
} |
||||
remove_dir_all(&dir).unwrap(); |
||||
}); |
||||
}); |
||||
} |
||||
group.finish(); |
||||
} |
||||
|
||||
fn create_quads(size: u64) -> Vec<Quad> { |
||||
(0..size) |
||||
.map(|_| { |
||||
Quad::new( |
||||
NamedNode::new_unchecked(format!( |
||||
"http://example.com/id/{}", |
||||
random::<u64>() % size |
||||
)), |
||||
NamedNode::new_unchecked(format!( |
||||
"http://example.com/id/{}", |
||||
random::<u64>() % size |
||||
)), |
||||
NamedNode::new_unchecked(format!( |
||||
"http://example.com/id/{}", |
||||
random::<u64>() % size |
||||
)), |
||||
None, |
||||
) |
||||
}) |
||||
.collect() |
||||
} |
@ -0,0 +1,637 @@ |
||||
use crate::model::*; |
||||
use crate::sparql::{GraphPattern, PreparedQuery, QueryOptions, SimplePreparedQuery}; |
||||
use crate::store::numeric_encoder::*; |
||||
use crate::store::{load_dataset, load_graph, ReadableEncodedStore, WritableEncodedStore}; |
||||
use crate::{DatasetSyntax, GraphSyntax, Result}; |
||||
use sled::{Config, Iter, Tree}; |
||||
use std::io::BufRead; |
||||
use std::path::Path; |
||||
use std::str; |
||||
|
||||
/// Store based on the [Sled](https://sled.rs/) key-value database.
|
||||
/// It encodes a [RDF dataset](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-dataset) and allows to query and update it using SPARQL.
|
||||
///
|
||||
/// To use it, the `"sled"` feature needs to be activated.
|
||||
///
|
||||
/// Warning: quad insertions and deletions are not (yet) atomic.
|
||||
///
|
||||
/// Usage example:
|
||||
/// ```
|
||||
/// use oxigraph::model::*;
|
||||
/// use oxigraph::{Result, SledStore};
|
||||
/// use oxigraph::sparql::{PreparedQuery, QueryOptions, QueryResult};
|
||||
/// # use std::fs::remove_dir_all;
|
||||
///
|
||||
/// # {
|
||||
/// let store = SledStore::open("example.db")?;
|
||||
///
|
||||
/// // insertion
|
||||
/// let ex = NamedNode::parse("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
|
||||
/// let prepared_query = store.prepare_query("SELECT ?s WHERE { ?s ?p ?o }", QueryOptions::default())?;
|
||||
/// let results = prepared_query.exec()?;
|
||||
/// if let QueryResult::Bindings(results) = results {
|
||||
/// assert_eq!(results.into_values_iter().next().unwrap()?[0], Some(ex.into()));
|
||||
/// }
|
||||
/// #
|
||||
/// # }
|
||||
/// # remove_dir_all("example.db")?;
|
||||
/// # Result::Ok(())
|
||||
/// ```
|
||||
#[derive(Clone)] |
||||
pub struct SledStore { |
||||
id2str: Tree, |
||||
spog: Tree, |
||||
posg: Tree, |
||||
ospg: Tree, |
||||
gspo: Tree, |
||||
gpos: Tree, |
||||
gosp: Tree, |
||||
} |
||||
|
||||
//TODO: indexes for the default graph and indexes for the named graphs (no more Optional and space saving)
|
||||
|
||||
impl SledStore { |
||||
/// Opens a temporary `SledStore` that will be deleted after drop.
|
||||
pub fn new() -> Result<Self> { |
||||
Self::do_open(Config::new().temporary(true)) |
||||
} |
||||
|
||||
/// Opens a `SledStore`
|
||||
pub fn open(path: impl AsRef<Path>) -> Result<Self> { |
||||
Self::do_open(Config::new().path(path)) |
||||
} |
||||
|
||||
fn do_open(config: Config) -> Result<Self> { |
||||
let db = config.open()?; |
||||
let new = Self { |
||||
id2str: db.open_tree("id2str")?, |
||||
spog: db.open_tree("spog")?, |
||||
posg: db.open_tree("posg")?, |
||||
ospg: db.open_tree("ospg")?, |
||||
gspo: db.open_tree("gspo")?, |
||||
gpos: db.open_tree("gpos")?, |
||||
gosp: db.open_tree("gosp")?, |
||||
}; |
||||
(&new).set_first_strings()?; |
||||
Ok(new) |
||||
} |
||||
|
||||
/// Prepares a [SPARQL 1.1 query](https://www.w3.org/TR/sparql11-query/) and returns an object that could be used to execute it.
|
||||
///
|
||||
/// See `MemoryStore` for a usage example.
|
||||
pub fn prepare_query<'a>( |
||||
&'a self, |
||||
query: &str, |
||||
options: QueryOptions<'_>, |
||||
) -> Result<impl PreparedQuery + 'a> { |
||||
SimplePreparedQuery::new((*self).clone(), query, options) |
||||
} |
||||
|
||||
/// This is similar to `prepare_query`, but useful if a SPARQL query has already been parsed, which is the case when building `ServiceHandler`s for federated queries with `SERVICE` clauses. For examples, look in the tests.
|
||||
pub fn prepare_query_from_pattern<'a>( |
||||
&'a self, |
||||
graph_pattern: &GraphPattern, |
||||
options: QueryOptions<'_>, |
||||
) -> Result<impl PreparedQuery + 'a> { |
||||
SimplePreparedQuery::new_from_pattern((*self).clone(), graph_pattern, options) |
||||
} |
||||
|
||||
/// Retrieves quads with a filter on each quad component
|
||||
///
|
||||
/// See `MemoryStore` for a usage example.
|
||||
#[allow(clippy::option_option)] |
||||
pub fn quads_for_pattern( |
||||
&self, |
||||
subject: Option<&NamedOrBlankNode>, |
||||
predicate: Option<&NamedNode>, |
||||
object: Option<&Term>, |
||||
graph_name: Option<Option<&NamedOrBlankNode>>, |
||||
) -> impl Iterator<Item = Result<Quad>> { |
||||
let subject = subject.map(|s| s.into()); |
||||
let predicate = predicate.map(|p| p.into()); |
||||
let object = object.map(|o| o.into()); |
||||
let graph_name = graph_name.map(|g| g.map_or(ENCODED_DEFAULT_GRAPH, |g| g.into())); |
||||
let this = self.clone(); |
||||
self.encoded_quads_for_pattern_inner(subject, predicate, object, graph_name) |
||||
.map(move |quad| this.decode_quad(&quad?)) |
||||
} |
||||
|
||||
/// Checks if this store contains a given quad
|
||||
pub fn contains(&self, quad: &Quad) -> Result<bool> { |
||||
let quad = quad.into(); |
||||
self.contains_encoded(&quad) |
||||
} |
||||
|
||||
/// Loads a graph file (i.e. triples) into the store
|
||||
///
|
||||
/// Warning: This functions saves the triples in batch. If the parsing fails in the middle of the file,
|
||||
/// only a part of it may be written. Use a (memory greedy) transaction if you do not want that.
|
||||
///
|
||||
/// See `MemoryStore` for a usage example.
|
||||
pub fn load_graph( |
||||
&self, |
||||
reader: impl BufRead, |
||||
syntax: GraphSyntax, |
||||
to_graph_name: Option<&NamedOrBlankNode>, |
||||
base_iri: Option<&str>, |
||||
) -> Result<()> { |
||||
let mut store = self; |
||||
load_graph(&mut store, reader, syntax, to_graph_name, base_iri) |
||||
} |
||||
|
||||
/// Loads a dataset file (i.e. quads) into the store.
|
||||
///
|
||||
/// Warning: This functions saves the quads in batch. If the parsing fails in the middle of the file,
|
||||
/// only a part of it may be written. Use a (memory greedy) transaction if you do not want that.
|
||||
///
|
||||
/// See `MemoryStore` for a usage example.
|
||||
pub fn load_dataset( |
||||
&self, |
||||
reader: impl BufRead, |
||||
syntax: DatasetSyntax, |
||||
base_iri: Option<&str>, |
||||
) -> Result<()> { |
||||
let mut store = self; |
||||
load_dataset(&mut store, reader, syntax, base_iri) |
||||
} |
||||
|
||||
/// Adds a quad to this store.
|
||||
pub fn insert(&self, quad: &Quad) -> Result<()> { |
||||
let mut store = self; |
||||
let quad = store.encode_quad(quad)?; |
||||
store.insert_encoded(&quad) |
||||
} |
||||
|
||||
/// Removes a quad from this store.
|
||||
pub fn remove(&self, quad: &Quad) -> Result<()> { |
||||
let mut store = self; |
||||
let quad = quad.into(); |
||||
store.remove_encoded(&quad) |
||||
} |
||||
|
||||
fn contains_encoded(&self, quad: &EncodedQuad) -> Result<bool> { |
||||
let mut buffer = Vec::with_capacity(4 * WRITTEN_TERM_MAX_SIZE); |
||||
write_spog_quad(&mut buffer, quad); |
||||
Ok(self.spog.contains_key(buffer)?) |
||||
} |
||||
|
||||
fn encoded_quads_for_pattern_inner( |
||||
&self, |
||||
subject: Option<EncodedTerm>, |
||||
predicate: Option<EncodedTerm>, |
||||
object: Option<EncodedTerm>, |
||||
graph_name: Option<EncodedTerm>, |
||||
) -> DecodingQuadIterator { |
||||
match subject { |
||||
Some(subject) => match predicate { |
||||
Some(predicate) => match object { |
||||
Some(object) => match graph_name { |
||||
Some(graph_name) => self |
||||
.spog_quads(encode_term_quad(subject, predicate, object, graph_name)), |
||||
None => self.quads_for_subject_predicate_object(subject, predicate, object), |
||||
}, |
||||
None => match graph_name { |
||||
Some(graph_name) => { |
||||
self.quads_for_subject_predicate_graph(subject, predicate, graph_name) |
||||
} |
||||
None => self.quads_for_subject_predicate(subject, predicate), |
||||
}, |
||||
}, |
||||
None => match object { |
||||
Some(object) => match graph_name { |
||||
Some(graph_name) => { |
||||
self.quads_for_subject_object_graph(subject, object, graph_name) |
||||
} |
||||
None => self.quads_for_subject_object(subject, object), |
||||
}, |
||||
None => match graph_name { |
||||
Some(graph_name) => self.quads_for_subject_graph(subject, graph_name), |
||||
None => self.quads_for_subject(subject), |
||||
}, |
||||
}, |
||||
}, |
||||
None => match predicate { |
||||
Some(predicate) => match object { |
||||
Some(object) => match graph_name { |
||||
Some(graph_name) => { |
||||
self.quads_for_predicate_object_graph(predicate, object, graph_name) |
||||
} |
||||
None => self.quads_for_predicate_object(predicate, object), |
||||
}, |
||||
None => match graph_name { |
||||
Some(graph_name) => self.quads_for_predicate_graph(predicate, graph_name), |
||||
None => self.quads_for_predicate(predicate), |
||||
}, |
||||
}, |
||||
None => match object { |
||||
Some(object) => match graph_name { |
||||
Some(graph_name) => self.quads_for_object_graph(object, graph_name), |
||||
None => self.quads_for_object(object), |
||||
}, |
||||
None => match graph_name { |
||||
Some(graph_name) => self.quads_for_graph(graph_name), |
||||
None => self.quads(), |
||||
}, |
||||
}, |
||||
}, |
||||
} |
||||
} |
||||
|
||||
fn quads(&self) -> DecodingQuadIterator { |
||||
self.spog_quads(Vec::default()) |
||||
} |
||||
|
||||
fn quads_for_subject(&self, subject: EncodedTerm) -> DecodingQuadIterator { |
||||
self.spog_quads(encode_term(subject)) |
||||
} |
||||
|
||||
fn quads_for_subject_predicate( |
||||
&self, |
||||
subject: EncodedTerm, |
||||
predicate: EncodedTerm, |
||||
) -> DecodingQuadIterator { |
||||
self.spog_quads(encode_term_pair(subject, predicate)) |
||||
} |
||||
|
||||
fn quads_for_subject_predicate_object( |
||||
&self, |
||||
subject: EncodedTerm, |
||||
predicate: EncodedTerm, |
||||
object: EncodedTerm, |
||||
) -> DecodingQuadIterator { |
||||
self.spog_quads(encode_term_triple(subject, predicate, object)) |
||||
} |
||||
|
||||
fn quads_for_subject_object( |
||||
&self, |
||||
subject: EncodedTerm, |
||||
object: EncodedTerm, |
||||
) -> DecodingQuadIterator { |
||||
self.ospg_quads(encode_term_pair(object, subject)) |
||||
} |
||||
|
||||
fn quads_for_predicate(&self, predicate: EncodedTerm) -> DecodingQuadIterator { |
||||
self.posg_quads(encode_term(predicate)) |
||||
} |
||||
|
||||
fn quads_for_predicate_object( |
||||
&self, |
||||
predicate: EncodedTerm, |
||||
object: EncodedTerm, |
||||
) -> DecodingQuadIterator { |
||||
self.posg_quads(encode_term_pair(predicate, object)) |
||||
} |
||||
|
||||
fn quads_for_object(&self, object: EncodedTerm) -> DecodingQuadIterator { |
||||
self.ospg_quads(encode_term(object)) |
||||
} |
||||
|
||||
fn quads_for_graph(&self, graph_name: EncodedTerm) -> DecodingQuadIterator { |
||||
self.gspo_quads(encode_term(graph_name)) |
||||
} |
||||
|
||||
fn quads_for_subject_graph( |
||||
&self, |
||||
subject: EncodedTerm, |
||||
graph_name: EncodedTerm, |
||||
) -> DecodingQuadIterator { |
||||
self.gspo_quads(encode_term_pair(graph_name, subject)) |
||||
} |
||||
|
||||
fn quads_for_subject_predicate_graph( |
||||
&self, |
||||
subject: EncodedTerm, |
||||
predicate: EncodedTerm, |
||||
graph_name: EncodedTerm, |
||||
) -> DecodingQuadIterator { |
||||
self.gspo_quads(encode_term_triple(graph_name, subject, predicate)) |
||||
} |
||||
|
||||
fn quads_for_subject_object_graph( |
||||
&self, |
||||
subject: EncodedTerm, |
||||
object: EncodedTerm, |
||||
graph_name: EncodedTerm, |
||||
) -> DecodingQuadIterator { |
||||
self.gosp_quads(encode_term_triple(graph_name, object, subject)) |
||||
} |
||||
|
||||
fn quads_for_predicate_graph( |
||||
&self, |
||||
predicate: EncodedTerm, |
||||
graph_name: EncodedTerm, |
||||
) -> DecodingQuadIterator { |
||||
self.gpos_quads(encode_term_pair(graph_name, predicate)) |
||||
} |
||||
|
||||
fn quads_for_predicate_object_graph( |
||||
&self, |
||||
predicate: EncodedTerm, |
||||
object: EncodedTerm, |
||||
graph_name: EncodedTerm, |
||||
) -> DecodingQuadIterator { |
||||
self.gpos_quads(encode_term_triple(graph_name, predicate, object)) |
||||
} |
||||
|
||||
fn quads_for_object_graph( |
||||
&self, |
||||
object: EncodedTerm, |
||||
graph_name: EncodedTerm, |
||||
) -> DecodingQuadIterator { |
||||
self.gosp_quads(encode_term_pair(graph_name, object)) |
||||
} |
||||
|
||||
fn spog_quads(&self, prefix: Vec<u8>) -> DecodingQuadIterator { |
||||
self.inner_quads(&self.spog, prefix, QuadEncoding::SPOG) |
||||
} |
||||
|
||||
fn posg_quads(&self, prefix: Vec<u8>) -> DecodingQuadIterator { |
||||
self.inner_quads(&self.posg, prefix, QuadEncoding::POSG) |
||||
} |
||||
|
||||
fn ospg_quads(&self, prefix: Vec<u8>) -> DecodingQuadIterator { |
||||
self.inner_quads(&self.ospg, prefix, QuadEncoding::OSPG) |
||||
} |
||||
|
||||
fn gspo_quads(&self, prefix: Vec<u8>) -> DecodingQuadIterator { |
||||
self.inner_quads(&self.gspo, prefix, QuadEncoding::GSPO) |
||||
} |
||||
|
||||
fn gpos_quads(&self, prefix: Vec<u8>) -> DecodingQuadIterator { |
||||
self.inner_quads(&self.gpos, prefix, QuadEncoding::GPOS) |
||||
} |
||||
|
||||
fn gosp_quads(&self, prefix: Vec<u8>) -> DecodingQuadIterator { |
||||
self.inner_quads(&self.gosp, prefix, QuadEncoding::GOSP) |
||||
} |
||||
|
||||
fn inner_quads( |
||||
&self, |
||||
tree: &Tree, |
||||
prefix: Vec<u8>, |
||||
order: QuadEncoding, |
||||
) -> DecodingQuadIterator { |
||||
DecodingQuadIterator { |
||||
iter: tree.scan_prefix(prefix), |
||||
order, |
||||
} |
||||
} |
||||
} |
||||
|
||||
impl StrLookup for SledStore { |
||||
fn get_str(&self, id: StrHash) -> Result<Option<String>> { |
||||
Ok(self |
||||
.id2str |
||||
.get(id.to_be_bytes())? |
||||
.map(|v| String::from_utf8(v.to_vec())) |
||||
.transpose()?) |
||||
} |
||||
} |
||||
|
||||
impl ReadableEncodedStore for SledStore { |
||||
fn encoded_quads_for_pattern<'a>( |
||||
&'a self, |
||||
subject: Option<EncodedTerm>, |
||||
predicate: Option<EncodedTerm>, |
||||
object: Option<EncodedTerm>, |
||||
graph_name: Option<EncodedTerm>, |
||||
) -> Box<dyn Iterator<Item = Result<EncodedQuad>> + 'a> { |
||||
Box::new(self.encoded_quads_for_pattern_inner(subject, predicate, object, graph_name)) |
||||
} |
||||
} |
||||
|
||||
impl<'a> StrContainer for &'a SledStore { |
||||
fn insert_str(&mut self, key: StrHash, value: &str) -> Result<()> { |
||||
self.id2str.insert(key.to_be_bytes(), value)?; |
||||
Ok(()) |
||||
} |
||||
} |
||||
|
||||
impl<'a> WritableEncodedStore for &'a SledStore { |
||||
fn insert_encoded(&mut self, quad: &EncodedQuad) -> Result<()> { |
||||
//TODO: atomicity
|
||||
let mut buffer = Vec::with_capacity(4 * WRITTEN_TERM_MAX_SIZE); |
||||
|
||||
write_spog_quad(&mut buffer, quad); |
||||
self.spog.insert(&buffer, &[])?; |
||||
buffer.clear(); |
||||
|
||||
write_posg_quad(&mut buffer, quad); |
||||
self.posg.insert(&buffer, &[])?; |
||||
buffer.clear(); |
||||
|
||||
write_ospg_quad(&mut buffer, quad); |
||||
self.ospg.insert(&buffer, &[])?; |
||||
buffer.clear(); |
||||
|
||||
write_gspo_quad(&mut buffer, quad); |
||||
self.gspo.insert(&buffer, &[])?; |
||||
buffer.clear(); |
||||
|
||||
write_gpos_quad(&mut buffer, quad); |
||||
self.gpos.insert(&buffer, &[])?; |
||||
buffer.clear(); |
||||
|
||||
write_gosp_quad(&mut buffer, quad); |
||||
self.gosp.insert(&buffer, &[])?; |
||||
buffer.clear(); |
||||
|
||||
Ok(()) |
||||
} |
||||
|
||||
fn remove_encoded(&mut self, quad: &EncodedQuad) -> Result<()> { |
||||
//TODO: atomicity
|
||||
let mut buffer = Vec::with_capacity(4 * WRITTEN_TERM_MAX_SIZE); |
||||
|
||||
write_spog_quad(&mut buffer, quad); |
||||
self.spog.remove(&buffer)?; |
||||
buffer.clear(); |
||||
|
||||
write_posg_quad(&mut buffer, quad); |
||||
self.posg.remove(&buffer)?; |
||||
buffer.clear(); |
||||
|
||||
write_ospg_quad(&mut buffer, quad); |
||||
self.ospg.remove(&buffer)?; |
||||
buffer.clear(); |
||||
|
||||
write_gspo_quad(&mut buffer, quad); |
||||
self.gspo.remove(&buffer)?; |
||||
buffer.clear(); |
||||
|
||||
write_gpos_quad(&mut buffer, quad); |
||||
self.gpos.remove(&buffer)?; |
||||
buffer.clear(); |
||||
|
||||
write_gosp_quad(&mut buffer, quad); |
||||
self.gosp.remove(&buffer)?; |
||||
buffer.clear(); |
||||
|
||||
Ok(()) |
||||
} |
||||
} |
||||
|
||||
fn encode_term(t: EncodedTerm) -> Vec<u8> { |
||||
let mut vec = Vec::with_capacity(WRITTEN_TERM_MAX_SIZE); |
||||
write_term(&mut vec, t); |
||||
vec |
||||
} |
||||
|
||||
fn encode_term_pair(t1: EncodedTerm, t2: EncodedTerm) -> Vec<u8> { |
||||
let mut vec = Vec::with_capacity(2 * WRITTEN_TERM_MAX_SIZE); |
||||
write_term(&mut vec, t1); |
||||
write_term(&mut vec, t2); |
||||
vec |
||||
} |
||||
|
||||
fn encode_term_triple(t1: EncodedTerm, t2: EncodedTerm, t3: EncodedTerm) -> Vec<u8> { |
||||
let mut vec = Vec::with_capacity(3 * WRITTEN_TERM_MAX_SIZE); |
||||
write_term(&mut vec, t1); |
||||
write_term(&mut vec, t2); |
||||
write_term(&mut vec, t3); |
||||
vec |
||||
} |
||||
|
||||
fn encode_term_quad(t1: EncodedTerm, t2: EncodedTerm, t3: EncodedTerm, t4: EncodedTerm) -> Vec<u8> { |
||||
let mut vec = Vec::with_capacity(4 * WRITTEN_TERM_MAX_SIZE); |
||||
write_term(&mut vec, t1); |
||||
write_term(&mut vec, t2); |
||||
write_term(&mut vec, t3); |
||||
write_term(&mut vec, t4); |
||||
vec |
||||
} |
||||
|
||||
struct DecodingQuadIterator { |
||||
iter: Iter, |
||||
order: QuadEncoding, |
||||
} |
||||
|
||||
impl Iterator for DecodingQuadIterator { |
||||
type Item = Result<EncodedQuad>; |
||||
|
||||
fn next(&mut self) -> Option<Result<EncodedQuad>> { |
||||
Some(match self.iter.next()? { |
||||
Ok((encoded, _)) => self.order.decode(&encoded), |
||||
Err(error) => Err(error.into()), |
||||
}) |
||||
} |
||||
} |
||||
|
||||
#[test] |
||||
fn store() -> Result<()> { |
||||
use crate::model::*; |
||||
use crate::*; |
||||
|
||||
let main_s = NamedOrBlankNode::from(BlankNode::default()); |
||||
let main_p = NamedNode::parse("http://example.com")?; |
||||
let main_o = Term::from(Literal::from(1)); |
||||
|
||||
let main_quad = Quad::new(main_s.clone(), main_p.clone(), main_o.clone(), None); |
||||
let all_o = vec![ |
||||
Quad::new(main_s.clone(), main_p.clone(), Literal::from(0), None), |
||||
Quad::new(main_s.clone(), main_p.clone(), main_o.clone(), None), |
||||
Quad::new(main_s.clone(), main_p.clone(), Literal::from(2), None), |
||||
]; |
||||
|
||||
let store = SledStore::new()?; |
||||
store.insert(&main_quad)?; |
||||
for t in &all_o { |
||||
store.insert(t)?; |
||||
} |
||||
|
||||
let target = vec![main_quad]; |
||||
assert_eq!( |
||||
store |
||||
.quads_for_pattern(None, None, None, None) |
||||
.collect::<Result<Vec<_>>>()?, |
||||
all_o |
||||
); |
||||
assert_eq!( |
||||
store |
||||
.quads_for_pattern(Some(&main_s), None, None, None) |
||||
.collect::<Result<Vec<_>>>()?, |
||||
all_o |
||||
); |
||||
assert_eq!( |
||||
store |
||||
.quads_for_pattern(Some(&main_s), Some(&main_p), None, None) |
||||
.collect::<Result<Vec<_>>>()?, |
||||
all_o |
||||
); |
||||
assert_eq!( |
||||
store |
||||
.quads_for_pattern(Some(&main_s), Some(&main_p), Some(&main_o), None) |
||||
.collect::<Result<Vec<_>>>()?, |
||||
target |
||||
); |
||||
assert_eq!( |
||||
store |
||||
.quads_for_pattern(Some(&main_s), Some(&main_p), Some(&main_o), Some(None)) |
||||
.collect::<Result<Vec<_>>>()?, |
||||
target |
||||
); |
||||
assert_eq!( |
||||
store |
||||
.quads_for_pattern(Some(&main_s), Some(&main_p), None, Some(None)) |
||||
.collect::<Result<Vec<_>>>()?, |
||||
all_o |
||||
); |
||||
assert_eq!( |
||||
store |
||||
.quads_for_pattern(Some(&main_s), None, Some(&main_o), None) |
||||
.collect::<Result<Vec<_>>>()?, |
||||
target |
||||
); |
||||
assert_eq!( |
||||
store |
||||
.quads_for_pattern(Some(&main_s), None, Some(&main_o), Some(None)) |
||||
.collect::<Result<Vec<_>>>()?, |
||||
target |
||||
); |
||||
assert_eq!( |
||||
store |
||||
.quads_for_pattern(Some(&main_s), None, None, Some(None)) |
||||
.collect::<Result<Vec<_>>>()?, |
||||
all_o |
||||
); |
||||
assert_eq!( |
||||
store |
||||
.quads_for_pattern(None, Some(&main_p), None, None) |
||||
.collect::<Result<Vec<_>>>()?, |
||||
all_o |
||||
); |
||||
assert_eq!( |
||||
store |
||||
.quads_for_pattern(None, Some(&main_p), Some(&main_o), None) |
||||
.collect::<Result<Vec<_>>>()?, |
||||
target |
||||
); |
||||
assert_eq!( |
||||
store |
||||
.quads_for_pattern(None, None, Some(&main_o), None) |
||||
.collect::<Result<Vec<_>>>()?, |
||||
target |
||||
); |
||||
assert_eq!( |
||||
store |
||||
.quads_for_pattern(None, None, None, Some(None)) |
||||
.collect::<Result<Vec<_>>>()?, |
||||
all_o |
||||
); |
||||
assert_eq!( |
||||
store |
||||
.quads_for_pattern(None, Some(&main_p), Some(&main_o), Some(None)) |
||||
.collect::<Result<Vec<_>>>()?, |
||||
target |
||||
); |
||||
|
||||
Ok(()) |
||||
} |
Loading…
Reference in new issue