use std::collections::BTreeMap;
pub type TripleKey = (u32, u32, u32);
pub struct BTreeIndex {
spo: BTreeMap<TripleKey, ()>,
pos: BTreeMap<(u32, u32, u32), ()>,
osp: BTreeMap<(u32, u32, u32), ()>,
}
impl BTreeIndex {
pub fn new() -> Self {
BTreeIndex {
spo: BTreeMap::new(),
pos: BTreeMap::new(),
osp: BTreeMap::new(),
}
}
pub fn insert(&mut self, s: u32, p: u32, o: u32) -> bool {
if self.spo.contains_key(&(s, p, o)) {
return false;
}
self.spo.insert((s, p, o), ());
self.pos.insert((p, o, s), ());
self.osp.insert((o, s, p), ());
true
}
pub fn remove(&mut self, s: u32, p: u32, o: u32) -> bool {
if !self.spo.contains_key(&(s, p, o)) {
return false;
}
self.spo.remove(&(s, p, o));
self.pos.remove(&(p, o, s));
self.osp.remove(&(o, s, p));
true
}
pub fn contains(&self, s: u32, p: u32, o: u32) -> bool {
self.spo.contains_key(&(s, p, o))
}
pub fn count(&self) -> usize {
self.spo.len()
}
pub fn match_pattern(&self, s: Option<u32>, p: Option<u32>, o: Option<u32>) -> Vec<TripleKey> {
match (s, p, o) {
(Some(sv), Some(pv), Some(ov)) => {
if self.spo.contains_key(&(sv, pv, ov)) {
vec![(sv, pv, ov)]
} else {
vec![]
}
}
(Some(sv), Some(pv), None) => self
.spo
.range((sv, pv, 0)..=(sv, pv, u32::MAX))
.map(|(k, _)| *k)
.collect(),
(Some(sv), None, None) => self
.spo
.range((sv, 0, 0)..=(sv, u32::MAX, u32::MAX))
.map(|(k, _)| *k)
.collect(),
(None, Some(pv), Some(ov)) => self
.pos
.range((pv, ov, 0)..=(pv, ov, u32::MAX))
.map(|(k, _)| (k.2, k.0, k.1))
.collect(),
(None, Some(pv), None) => self
.pos
.range((pv, 0, 0)..=(pv, u32::MAX, u32::MAX))
.map(|(k, _)| (k.2, k.0, k.1))
.collect(),
(None, None, Some(ov)) => self
.osp
.range((ov, 0, 0)..=(ov, u32::MAX, u32::MAX))
.map(|(k, _)| (k.1, k.2, k.0))
.collect(),
(Some(sv), None, Some(ov)) => self
.osp
.range((ov, sv, 0)..=(ov, sv, u32::MAX))
.map(|(k, _)| (k.1, k.2, k.0))
.collect(),
(None, None, None) => self.spo.keys().copied().collect(),
}
}
pub fn subjects(&self) -> Vec<u32> {
let mut seen: Vec<u32> = Vec::new();
let mut last: Option<u32> = None;
for (s, _, _) in self.spo.keys() {
if last != Some(*s) {
seen.push(*s);
last = Some(*s);
}
}
seen
}
pub fn predicates(&self) -> Vec<u32> {
let mut seen: Vec<u32> = Vec::new();
let mut last: Option<u32> = None;
for (p, _, _) in self.pos.keys() {
if last != Some(*p) {
seen.push(*p);
last = Some(*p);
}
}
seen
}
pub fn objects(&self) -> Vec<u32> {
let mut seen: Vec<u32> = Vec::new();
let mut last: Option<u32> = None;
for (o, _, _) in self.osp.keys() {
if last != Some(*o) {
seen.push(*o);
last = Some(*o);
}
}
seen
}
pub fn clear(&mut self) {
self.spo.clear();
self.pos.clear();
self.osp.clear();
}
pub fn iter_spo(&self) -> impl Iterator<Item = &TripleKey> {
self.spo.keys()
}
}
impl Default for BTreeIndex {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
fn make_index() -> BTreeIndex {
let mut idx = BTreeIndex::new();
idx.insert(1, 10, 100);
idx.insert(1, 10, 101);
idx.insert(1, 11, 200);
idx.insert(2, 10, 100);
idx.insert(2, 12, 300);
idx.insert(3, 13, 400);
idx
}
#[test]
fn test_new_empty() {
let idx = BTreeIndex::new();
assert_eq!(idx.count(), 0);
}
#[test]
fn test_insert_returns_true() {
let mut idx = BTreeIndex::new();
assert!(idx.insert(1, 2, 3));
}
#[test]
fn test_insert_duplicate_returns_false() {
let mut idx = BTreeIndex::new();
idx.insert(1, 2, 3);
assert!(!idx.insert(1, 2, 3));
}
#[test]
fn test_count_after_insert() {
let idx = make_index();
assert_eq!(idx.count(), 6);
}
#[test]
fn test_contains_existing() {
let idx = make_index();
assert!(idx.contains(1, 10, 100));
}
#[test]
fn test_contains_missing() {
let idx = make_index();
assert!(!idx.contains(99, 99, 99));
}
#[test]
fn test_remove_existing() {
let mut idx = make_index();
assert!(idx.remove(1, 10, 100));
assert!(!idx.contains(1, 10, 100));
assert_eq!(idx.count(), 5);
}
#[test]
fn test_remove_missing() {
let mut idx = make_index();
assert!(!idx.remove(99, 99, 99));
}
#[test]
fn test_clear() {
let mut idx = make_index();
idx.clear();
assert_eq!(idx.count(), 0);
}
#[test]
fn test_pattern_all_bound_found() {
let idx = make_index();
let res = idx.match_pattern(Some(1), Some(10), Some(100));
assert_eq!(res, vec![(1, 10, 100)]);
}
#[test]
fn test_pattern_all_bound_not_found() {
let idx = make_index();
let res = idx.match_pattern(Some(1), Some(10), Some(999));
assert!(res.is_empty());
}
#[test]
fn test_pattern_s_p_bound() {
let idx = make_index();
let mut res = idx.match_pattern(Some(1), Some(10), None);
res.sort();
assert_eq!(res, vec![(1, 10, 100), (1, 10, 101)]);
}
#[test]
fn test_pattern_s_bound_only() {
let idx = make_index();
let mut res = idx.match_pattern(Some(1), None, None);
res.sort();
assert_eq!(res, vec![(1, 10, 100), (1, 10, 101), (1, 11, 200)]);
}
#[test]
fn test_pattern_p_o_bound() {
let idx = make_index();
let mut res = idx.match_pattern(None, Some(10), Some(100));
res.sort();
assert_eq!(res, vec![(1, 10, 100), (2, 10, 100)]);
}
#[test]
fn test_pattern_p_bound_only() {
let idx = make_index();
let mut res = idx.match_pattern(None, Some(10), None);
res.sort();
assert_eq!(res, vec![(1, 10, 100), (1, 10, 101), (2, 10, 100)]);
}
#[test]
fn test_pattern_o_bound_only() {
let idx = make_index();
let mut res = idx.match_pattern(None, None, Some(100));
res.sort();
assert_eq!(res, vec![(1, 10, 100), (2, 10, 100)]);
}
#[test]
fn test_pattern_s_o_bound() {
let idx = make_index();
let mut res = idx.match_pattern(Some(1), None, Some(100));
res.sort();
assert_eq!(res, vec![(1, 10, 100)]);
}
#[test]
fn test_pattern_all_wildcard() {
let idx = make_index();
let res = idx.match_pattern(None, None, None);
assert_eq!(res.len(), 6);
}
#[test]
fn test_pattern_empty_result() {
let idx = make_index();
let res = idx.match_pattern(Some(999), None, None);
assert!(res.is_empty());
}
#[test]
fn test_subjects() {
let idx = make_index();
let subjs = idx.subjects();
assert_eq!(subjs, vec![1, 2, 3]);
}
#[test]
fn test_predicates() {
let idx = make_index();
let preds = idx.predicates();
assert_eq!(preds, vec![10, 11, 12, 13]);
}
#[test]
fn test_objects() {
let idx = make_index();
let objs = idx.objects();
assert_eq!(objs, vec![100, 101, 200, 300, 400]);
}
#[test]
fn test_subjects_empty() {
let idx = BTreeIndex::new();
assert!(idx.subjects().is_empty());
}
#[test]
fn test_predicates_empty() {
let idx = BTreeIndex::new();
assert!(idx.predicates().is_empty());
}
#[test]
fn test_objects_empty() {
let idx = BTreeIndex::new();
assert!(idx.objects().is_empty());
}
#[test]
fn test_iter_spo_order() {
let idx = make_index();
let triples: Vec<TripleKey> = idx.iter_spo().copied().collect();
for w in triples.windows(2) {
assert!(w[0] <= w[1]);
}
}
#[test]
fn test_insert_then_remove_all_indices_consistent() {
let mut idx = BTreeIndex::new();
idx.insert(5, 50, 500);
idx.remove(5, 50, 500);
assert_eq!(idx.count(), 0);
assert!(idx.match_pattern(None, Some(50), None).is_empty());
assert!(idx.match_pattern(None, None, Some(500)).is_empty());
}
#[test]
fn test_default_trait() {
let idx = BTreeIndex::default();
assert_eq!(idx.count(), 0);
}
#[test]
fn test_large_insert() {
let mut idx = BTreeIndex::new();
for s in 0..10u32 {
for p in 0..10u32 {
for o in 0..10u32 {
idx.insert(s, p, o);
}
}
}
assert_eq!(idx.count(), 1000);
assert_eq!(idx.subjects().len(), 10);
assert_eq!(idx.predicates().len(), 10);
assert_eq!(idx.objects().len(), 10);
}
#[test]
fn test_pattern_p_bound_returns_all_with_pred() {
let mut idx = BTreeIndex::new();
idx.insert(1, 5, 10);
idx.insert(2, 5, 20);
idx.insert(3, 6, 30);
let res = idx.match_pattern(None, Some(5), None);
assert_eq!(res.len(), 2);
}
#[test]
fn test_remove_middle_triple() {
let mut idx = make_index();
idx.remove(2, 10, 100);
let res = idx.match_pattern(Some(2), None, None);
assert_eq!(res.len(), 1);
assert_eq!(res[0], (2, 12, 300));
}
}