use context::*;
use error::*;
use subject::{Subject,SubjectType,SUBJECT_MAX_RELATIONS};
use subjecthandle::SubjectHandle;
use slab::*;
use memorefhead::MemoRefHead;
use error::RetrieveError;
use std::collections::HashMap;
pub struct IndexFixed {
root: Subject,
depth: u8
}
impl IndexFixed {
pub fn new (context: &Context, depth: u8) -> Result<IndexFixed,WriteError> {
Ok(Self {
root: Subject::new( context, SubjectType::IndexNode, HashMap::new() )?,
depth: depth
})
}
pub fn new_from_memorefhead (context: &Context, depth: u8, memorefhead: MemoRefHead ) -> IndexFixed {
Self {
root: Subject::reconstitute( context, memorefhead ).unwrap(),
depth: depth
}
}
pub fn insert_subject_handle (&self, key: u64, subjecthandle: &SubjectHandle) -> Result<(),WriteError> {
self.insert(&subjecthandle.context, key, &subjecthandle.subject)
}
pub (crate) fn insert <'a> (&self, context: &Context, key: u64, subject: &Subject) -> Result<(),WriteError> {
let node = &self.root;
self.recurse_set(context, 0, key, node, subject)
}
fn recurse_set(&self, context: &Context, tier: usize, key: u64, node: &Subject, subject: &Subject) -> Result<(),WriteError>{
let exponent : u32 = (self.depth as u32 - 1) - tier as u32;
let x = SUBJECT_MAX_RELATIONS.pow(exponent as u32);
let y = ((key / (x as u64)) % SUBJECT_MAX_RELATIONS as u64) as RelationSlotId;
if exponent == 0 {
node.set_edge(context, y as RelationSlotId,&subject)
}else{
match node.get_edge(context,y)? {
Some(n) => {
self.recurse_set(context, tier+1, key, &n, subject)
}
None => {
let mut values = HashMap::new();
values.insert("tier".to_string(),tier.to_string());
let new_node = Subject::new( context, SubjectType::IndexNode, values )?;
node.set_edge(context, y, &new_node)?;
self.recurse_set(context, tier+1, key, &new_node, subject)
}
}
}
}
pub fn get_root_subject_handle(&self, context: &Context) -> Result<SubjectHandle,RetrieveError> {
Ok(SubjectHandle{
id: self.root.id,
subject: self.root.clone(),
context: context.clone()
})
}
pub fn get_subject_handle(&self, context: &Context, key: u64 ) -> Result<Option<SubjectHandle>,RetrieveError> {
match self.get(context,key)? {
Some(subject) => {
Ok(Some(SubjectHandle{
id: subject.id,
subject: subject,
context: context.clone()
}))
},
None => Ok(None)
}
}
pub (crate) fn get ( &self, context: &Context, key: u64 ) -> Result<Option<Subject>, RetrieveError> {
match self.get_head( context, key )? {
Some(mrh) => Ok(Some( context.get_subject_with_head( mrh )? )),
None => Ok(None)
}
}
pub (crate) fn get_head ( &self, context: &Context, key: u64 ) -> Result<Option<MemoRefHead>, RetrieveError> {
let mut node = self.root.clone();
let max = SUBJECT_MAX_RELATIONS as u64;
for tier in 0..self.depth {
let exponent = (self.depth - 1) - tier;
let x = max.pow(exponent as u32);
let y = ((key / (x as u64)) % max) as RelationSlotId;
if exponent == 0 {
return node.get_edge_head( context, y as RelationSlotId);
}else{
match node.get_edge( context, y)? {
Some(n) => node = n,
None => return Ok(None),
}
}
};
panic!("Sanity error");
}
pub fn scan_kv( &self, context: &Context, key: &str, value: &str ) -> Result<Option<SubjectHandle>, RetrieveError> {
self.scan(&context, |r| {
if let Some(v) = r.get_value(key) {
Ok(v == value)
}else{
Ok(false)
}
})
}
pub (crate) fn scan<F> ( &self, context: &Context, f: F ) -> Result<Option<SubjectHandle>, RetrieveError>
where F: Fn( &SubjectHandle ) -> Result<bool,RetrieveError> {
let node = self.root.clone();
self.scan_recurse( context, &node, 0, &f )
}
fn scan_recurse <F> ( &self, context: &Context, node: &Subject, tier: usize, f: &F ) -> Result<Option<SubjectHandle>, RetrieveError>
where F: Fn( &SubjectHandle ) -> Result<bool,RetrieveError> {
if tier as u8 == self.depth - 1 {
for slot_id in 0..SUBJECT_MAX_RELATIONS {
if let Some(mrh) = node.get_edge_head( context, slot_id as RelationSlotId )? {
let sh = context.get_subject_handle_with_head(mrh)?;
if f(&sh)? {
return Ok(Some(sh))
}
}
}
}else{
for slot_id in 0..SUBJECT_MAX_RELATIONS {
if let Some(child) = node.get_edge(context,slot_id as RelationSlotId)? {
if let Some(mrh) = self.scan_recurse(context, &child, tier + 1, f)? {
return Ok(Some(mrh))
}
}
}
}
Ok(None)
}
}
#[cfg(test)]
mod test {
use {Network, Slab, SubjectHandle};
use super::IndexFixed;
#[test]
fn index_construction() {
let net = Network::create_new_system();
let context_a = Slab::new(&net).create_context();
let index = IndexFixed::new(&context_a, 5).unwrap();
let i = 12345;
let record = SubjectHandle::new_kv(&context_a, "record number", &format!("{}",i)).unwrap();
index.insert_subject_handle(i, &record).unwrap();
assert_eq!( index.get_subject_handle(&context_a,12345).unwrap().unwrap().get_value("record number").unwrap(), "12345");
for i in 0..500 {
let record = SubjectHandle::new_kv(&context_a, "record number", &format!("{}",i)).unwrap();
index.insert_subject_handle(i, &record).unwrap();
}
for i in 0..500 {
assert_eq!( index.get_subject_handle(&context_a,i).unwrap().unwrap().get_value("record number").unwrap(), i.to_string() );
}
let maybe_rec = index.scan_kv(&context_a, "record number","12345").unwrap();
assert!( maybe_rec.is_some(), "Index scan for record 12345" );
assert_eq!( maybe_rec.unwrap().get_value("record number").unwrap(), "12345", "Is correct record");
let maybe_rec = index.scan_kv(&context_a, "record number","275").unwrap();
assert!( maybe_rec.is_some(), "Index scan for record 275" );
assert_eq!( maybe_rec.unwrap().get_value("record number").unwrap(), "275", "Is correct record");
}
}