use crate::model::TermRef; use crate::sparql::algebra::QueryDataset; use crate::sparql::EvaluationError; use crate::storage::numeric_encoder::{insert_term, EncodedQuad, EncodedTerm, StrHash, StrLookup}; use crate::storage::{StorageError, StorageReader}; use std::cell::RefCell; use std::collections::hash_map::Entry; use std::collections::HashMap; use std::iter::empty; pub struct DatasetView { reader: StorageReader, extra: RefCell>, dataset: EncodedDatasetSpec, } impl DatasetView { pub fn new(reader: StorageReader, dataset: &QueryDataset) -> Self { let dataset = EncodedDatasetSpec { default: dataset .default_graph_graphs() .map(|graphs| graphs.iter().map(|g| g.as_ref().into()).collect::>()), named: dataset .available_named_graphs() .map(|graphs| graphs.iter().map(|g| g.as_ref().into()).collect::>()), }; Self { reader, extra: RefCell::new(HashMap::default()), dataset, } } fn store_encoded_quads_for_pattern( &self, subject: Option<&EncodedTerm>, predicate: Option<&EncodedTerm>, object: Option<&EncodedTerm>, graph_name: Option<&EncodedTerm>, ) -> impl Iterator> + 'static { self.reader .quads_for_pattern(subject, predicate, object, graph_name) .map(|t| t.map_err(|e| e.into())) } #[allow(clippy::needless_collect)] pub fn encoded_quads_for_pattern( &self, subject: Option<&EncodedTerm>, predicate: Option<&EncodedTerm>, object: Option<&EncodedTerm>, graph_name: Option<&EncodedTerm>, ) -> Box>> { if let Some(graph_name) = graph_name { if graph_name.is_default_graph() { if let Some(default_graph_graphs) = &self.dataset.default { if default_graph_graphs.len() == 1 { // Single graph optimization Box::new( self.store_encoded_quads_for_pattern( subject, predicate, object, Some(&default_graph_graphs[0]), ) .map(|quad| { let quad = quad?; Ok(EncodedQuad::new( quad.subject, quad.predicate, quad.object, EncodedTerm::DefaultGraph, )) }), ) } else { let iters = default_graph_graphs .iter() .map(|graph_name| { self.store_encoded_quads_for_pattern( subject, predicate, object, Some(graph_name), ) }) .collect::>(); Box::new(iters.into_iter().flatten().map(|quad| { let quad = quad?; Ok(EncodedQuad::new( quad.subject, quad.predicate, quad.object, EncodedTerm::DefaultGraph, )) })) } } else { Box::new( self.store_encoded_quads_for_pattern(subject, predicate, object, None) .map(|quad| { let quad = quad?; Ok(EncodedQuad::new( quad.subject, quad.predicate, quad.object, EncodedTerm::DefaultGraph, )) }), ) } } else if self .dataset .named .as_ref() .map_or(true, |d| d.contains(graph_name)) { Box::new(self.store_encoded_quads_for_pattern( subject, predicate, object, Some(graph_name), )) } else { Box::new(empty()) } } else if let Some(named_graphs) = &self.dataset.named { let iters = named_graphs .iter() .map(|graph_name| { self.store_encoded_quads_for_pattern( subject, predicate, object, Some(graph_name), ) }) .collect::>(); Box::new(iters.into_iter().flatten()) } else { Box::new( self.store_encoded_quads_for_pattern(subject, predicate, object, None) .filter(|quad| match quad { Err(_) => true, Ok(quad) => !quad.graph_name.is_default_graph(), }), ) } } pub fn encode_term<'a>(&self, term: impl Into>) -> EncodedTerm { let term = term.into(); let encoded = term.into(); insert_term(term, &encoded, &mut |key, value| { self.insert_str(key, value); Ok(()) }) .unwrap(); encoded } pub fn insert_str(&self, key: &StrHash, value: &str) { if let Entry::Vacant(e) = self.extra.borrow_mut().entry(*key) { if !matches!(self.reader.contains_str(key), Ok(true)) { e.insert(value.to_owned()); } } } } impl StrLookup for DatasetView { fn get_str(&self, key: &StrHash) -> Result, StorageError> { Ok(if let Some(value) = self.extra.borrow().get(key) { Some(value.clone()) } else { self.reader.get_str(key)? }) } fn contains_str(&self, key: &StrHash) -> Result { Ok(self.extra.borrow().contains_key(key) || self.reader.contains_str(key)?) } } struct EncodedDatasetSpec { default: Option>, named: Option>, }