use crate::model::{Triple, NamedGraph, JsonLdDocument};
use std::collections::HashMap;
use anyhow::Result;
use smallvec::SmallVec;
#[derive(Debug, Clone, Default)]
pub struct GraphStore {
graphs: HashMap<String, NamedGraph>,
default_graph: NamedGraph,
subject_index: HashMap<String, SmallVec<[usize; 8]>>,
predicate_index: HashMap<String, SmallVec<[usize; 8]>>,
object_index: HashMap<String, SmallVec<[usize; 8]>>,
}
impl GraphStore {
pub fn new() -> Self {
Self {
graphs: HashMap::new(),
default_graph: NamedGraph {
name: "default".to_string(),
triples: Vec::new(),
},
subject_index: HashMap::new(),
predicate_index: HashMap::new(),
object_index: HashMap::new(),
}
}
pub fn add_triple(&mut self, triple: Triple) {
let index = self.default_graph.triples.len();
self.default_graph.triples.push(triple.clone());
self.subject_index.entry(triple.subject.clone()).or_insert_with(SmallVec::new).push(index);
self.predicate_index.entry(triple.predicate.clone()).or_insert_with(SmallVec::new).push(index);
self.object_index.entry(triple.object.clone()).or_insert_with(SmallVec::new).push(index);
}
pub fn add_triple_to_graph(&mut self, graph_name: &str, triple: Triple) {
self.graphs
.entry(graph_name.to_string())
.or_insert_with(|| NamedGraph {
name: graph_name.to_string(),
triples: Vec::new(),
})
.triples
.push(triple);
}
pub fn add_triples_to_graph(&mut self, graph_name: &str, triples: Vec<Triple>) {
self.graphs
.entry(graph_name.to_string())
.or_insert_with(|| NamedGraph {
name: graph_name.to_string(),
triples: Vec::new(),
})
.triples
.extend(triples);
}
pub fn get_default_graph(&self) -> &NamedGraph {
&self.default_graph
}
pub fn get_graph(&self, name: &str) -> Option<&NamedGraph> {
self.graphs.get(name)
}
pub fn graph_names(&self) -> Vec<String> {
self.graphs.keys().cloned().collect()
}
pub fn find_triples(&self, subject: Option<&str>, predicate: Option<&str>, object: Option<&str>) -> Vec<&Triple> {
let candidate_indices: SmallVec<[usize; 8]> = match (subject, predicate, object) {
(Some(s), Some(p), Some(o)) => {
self.find_exact_triple_indices(s, p, o)
},
(Some(s), None, None) => {
self.subject_index.get(s).cloned().unwrap_or(SmallVec::new())
},
(None, Some(p), None) => {
self.predicate_index.get(p).cloned().unwrap_or(SmallVec::new())
},
(None, None, Some(o)) => {
self.object_index.get(o).cloned().unwrap_or(SmallVec::new())
},
(Some(s), Some(p), None) => {
self.intersect_indices(
self.subject_index.get(s).map(|v| v.as_slice()).unwrap_or(&[]),
self.predicate_index.get(p).map(|v| v.as_slice()).unwrap_or(&[]),
)
},
(Some(s), None, Some(o)) => {
self.intersect_indices(
self.subject_index.get(s).map(|v| v.as_slice()).unwrap_or(&[]),
self.object_index.get(o).map(|v| v.as_slice()).unwrap_or(&[]),
)
},
(None, Some(p), Some(o)) => {
self.intersect_indices(
self.predicate_index.get(p).map(|v| v.as_slice()).unwrap_or(&[]),
self.object_index.get(o).map(|v| v.as_slice()).unwrap_or(&[]),
)
},
(None, None, None) => {
(0..self.default_graph.triples.len()).map(|i| i).collect()
}
};
let mut results = Vec::new();
for &index in &candidate_indices {
if let Some(triple) = self.default_graph.triples.get(index) {
results.push(triple);
}
}
results
}
fn find_exact_triple_indices(&self, subject: &str, predicate: &str, object: &str) -> SmallVec<[usize; 8]> {
if let Some(subject_indices) = self.subject_index.get(subject) {
if let Some(predicate_indices) = self.predicate_index.get(predicate) {
if let Some(object_indices) = self.object_index.get(object) {
let mut result = SmallVec::new();
for &idx in subject_indices {
if predicate_indices.contains(&idx) && object_indices.contains(&idx) {
result.push(idx);
}
}
return result;
}
}
}
SmallVec::new()
}
fn intersect_indices(&self, a: &[usize], b: &[usize]) -> SmallVec<[usize; 8]> {
let mut result = SmallVec::new();
let mut i = 0;
let mut j = 0;
while i < a.len() && j < b.len() {
match a[i].cmp(&b[j]) {
std::cmp::Ordering::Less => i += 1,
std::cmp::Ordering::Greater => j += 1,
std::cmp::Ordering::Equal => {
result.push(a[i]);
i += 1;
j += 1;
}
}
}
result
}
pub fn to_jsonld(&self) -> Result<JsonLdDocument> {
let mut graph = Vec::new();
for triple in &self.default_graph.triples {
let node = serde_json::json!({
"@id": triple.subject,
triple.predicate.clone(): triple.object
});
graph.push(node);
}
for named_graph in self.graphs.values() {
for triple in &named_graph.triples {
let node = serde_json::json!({
"@id": triple.subject,
triple.predicate.clone(): triple.object
});
graph.push(node);
}
}
Ok(JsonLdDocument {
context: serde_json::json!({
"@vocab": "https://w3id.org/security#",
"rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
"rdfs": "http://www.w3.org/2000/01/rdf-schema#"
}),
graph: Some(graph),
data: HashMap::new(),
})
}
pub fn clear(&mut self) {
self.graphs.clear();
self.default_graph.triples.clear();
self.subject_index.clear();
self.predicate_index.clear();
self.object_index.clear();
}
}