diff --git a/src/database/index.rs b/src/database/index.rs index 0d5f26f..42695a8 100644 --- a/src/database/index.rs +++ b/src/database/index.rs @@ -1,9 +1,78 @@ +use super::row::Row; use serde::{Deserialize, Serialize}; +use serde_json::Value; +use std::collections::HashMap; #[derive(Serialize, Deserialize)] pub struct Index { - pk: u64, + desc_sort: bool, key: String, - asd_sort: bool, unique: bool, + index_rows: HashMap>, +} + +impl Index { + pub fn new(key: String, desc_sort: Option, unique: Option) -> Index { + Index { + desc_sort: desc_sort.unwrap_or(false), + key, + unique: unique.unwrap_or(false), + index_rows: hashmap!(), + } + } + + pub fn insert_row(&mut self, pk_name: String, row: Row) -> Result<(), &str> { + let row_value = row.get(&self.key); + + // Check if the index row already exists + let index_row = if row_value.is_some() { + let str = serde_json::to_value(row_value.unwrap()); + + self.index_rows.get(&str.unwrap().to_string()) + } else { + Option::None + }; + + let key = serde_json::to_value(row_value.unwrap()) + .unwrap() + .to_string(); + + let pk_value = row.get(&pk_name).unwrap().as_u64().unwrap(); + + if index_row.is_none() { + // Insert the new row into the index + self.index_rows.insert(key, vec![pk_value]); + return Result::Ok(()); + } + + if self.unique { + return Result::Err("Index is unique!"); + } + + let mut vector = index_row.unwrap().clone(); + + vector.push(pk_value); + + vector.sort(); + + if self.desc_sort { + vector.reverse(); + } + + self.index_rows.insert(key, vector); + + return Result::Ok(()); + } + + pub fn get_pks_by_value(&self, value: Value) -> Option<&Vec> { + let key = serde_json::to_value(value).unwrap().to_string(); + + self.index_rows.get(&key) + } +} + +#[derive(Serialize, Deserialize)] +pub struct IndexRow { + pk: u64, + value: String, } diff --git a/src/database/mod.rs b/src/database/mod.rs index c6b7498..26fe017 100644 --- a/src/database/mod.rs +++ b/src/database/mod.rs @@ -39,14 +39,14 @@ impl Database { #[cfg(test)] mod tests { - use crate::database::{row::Row, table::Table, Database}; + use crate::database::{table::Table, Database}; use std::{fs, path::Path}; #[test] fn should_write_and_read_to_file() { - let mut table: Table = Table::new("Cats", "id"); - table.insert_row(Row::new(hashmapJson!["id" => 1, "name" => "Ozzy"])); - table.insert_row(Row::new(hashmapJson!["id" => 2, "name" => "Simon"])); + let mut table: Table = Table::new("Cats", "id", None); + let _ = table.insert_row(row!["id" => 1, "name" => "Ozzy"]); + _ = table.insert_row(row!["id" => 2, "name" => "Simon"]); let database = Database::new(hashmap!["Cats" => table]); database.to_file("./db.json"); @@ -63,7 +63,7 @@ mod tests { panic!("Unable to find table"); } - let row = table.unwrap().find_by_pk(&1u64); + let row = table.unwrap().find_by_pk(1u64); assert_eq!(row.is_ok(), true); // Cleanup file diff --git a/src/database/row.rs b/src/database/row.rs index 7dc9d15..61d2306 100644 --- a/src/database/row.rs +++ b/src/database/row.rs @@ -1,14 +1,4 @@ -use serde::{Deserialize, Serialize}; use serde_json::Value; use std::collections::HashMap; -#[derive(Serialize, Deserialize)] -pub struct Row { - pub columns: HashMap, -} - -impl Row { - pub fn new(cols: HashMap) -> Row { - Row { columns: cols } - } -} +pub type Row = HashMap; diff --git a/src/database/table.rs b/src/database/table.rs index 85f3c30..4eff558 100644 --- a/src/database/table.rs +++ b/src/database/table.rs @@ -1,51 +1,103 @@ use super::{index::Index, row::Row}; use serde::{Deserialize, Serialize}; -use std::{collections::HashMap, fs}; +use serde_json::Value; +use std::collections::{hash_map::Entry, HashMap}; #[derive(Serialize, Deserialize)] pub struct Table { - indexes: Vec, + indexes: HashMap, rows: HashMap, name: String, - pk_key: String, + pk: String, } impl Table { - pub fn new(name: &str, pk_key: &str) -> Table { + pub fn new(name: &str, pk: &str, indexes: Option>) -> Table { Table { - indexes: vec![], + indexes: indexes.unwrap_or(HashMap::new()), rows: HashMap::new(), name: name.to_string(), - pk_key: pk_key.to_string(), + pk: pk.to_string(), } } - pub fn create_index(&mut self, index: Index) { - // TODO: Add row indexes to the index itself before pushing to the table - self.indexes.push(index) - } - - pub fn find_by_pk(&self, value: &u64) -> Result<&Row, &'static str> { - let row = self.rows.get(value); - if row.is_none() { - return Err("Row not found!"); + pub fn create_index(&mut self, key: String, mut index: Index) { + for ele in self.rows.clone() { + let _ = index.insert_row(self.pk.clone(), ele.1.clone()); } - Ok(row.unwrap().clone()) + self.indexes.insert(key, index); } - pub fn insert_row(&mut self, row: Row) { - let key_option = row.columns.get(&self.pk_key); + pub fn insert_row(&mut self, row: Row) -> Result<(), &str> { + let key_option = row.get(&self.pk); if key_option.is_none() { panic!("Primary key not found on row to insert!"); } - let key = key_option.unwrap().as_u64(); + let row_primary_key = key_option.unwrap().as_u64(); - if key.is_none() { + if row_primary_key.is_none() { panic!("Primary key is not of type u64"); } - self.rows.insert(key.unwrap(), row); + // Insert row into database + self.rows.insert(row_primary_key.unwrap(), row.clone()); + + // Insert row into eventual indexes + for (key, _) in row.clone() { + let index_key = key.as_str().to_string(); + + match self.indexes.entry(index_key) { + Entry::Occupied(mut e) => { + let result = e.get_mut().insert_row(self.pk.clone(), row.clone()); + if result.is_err() { + panic!( + "Failed writing to index with erro: {}", + result.err().unwrap() + ); + } + } + Entry::Vacant(_e) => {} + } + } + + Result::Ok(()) + } + + pub fn find_by_pk(&self, value: u64) -> Result<&Row, String> { + let row = self.rows.get(&value); + if row.is_none() { + return Err(format!("Row with pk: {} not found!", value)); + } + Ok(row.unwrap()) + } + + pub fn find_by_pks(&self, value: Vec) -> Vec> { + let mut vector = vec![]; + for ele in value { + vector.push(self.find_by_pk(ele)); + } + return vector; + } + + pub fn get_pks_by_index(&self, index_key: String, value: Value) -> Result, String> { + // Attempt to get the primary keys for the index. + + if !self.indexes.contains_key(&index_key) { + // If it fails then return a error + + let formatted_error = + format!("Nothing found index not found: {}", index_key).to_string(); + return Err(formatted_error); + } + + return Ok(self + .indexes + .get(&index_key) + .unwrap() + .get_pks_by_value(value.clone()) + .unwrap() + .clone()); } } diff --git a/src/main.rs b/src/main.rs index 6830a81..db5162f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,10 +6,18 @@ macro_rules! hashmapJson { }} } +macro_rules! row { + ($( $key: expr => $val: expr ),*) => {{ + let mut map: crate::database::row::Row = ::std::collections::HashMap::new(); + $( map.insert($key.to_string(), serde_json::to_value($val).unwrap()); )* + map + }} +} + macro_rules! hashmap { ($( $key: expr => $val: expr ),*) => {{ let mut map = ::std::collections::HashMap::new(); - $( map.insert($key.to_string(), $val ))*; + $( map.insert($key.to_string(), $val );)* map }} } diff --git a/src/tests/index_test.rs b/src/tests/index_test.rs new file mode 100644 index 0000000..b5012fc --- /dev/null +++ b/src/tests/index_test.rs @@ -0,0 +1,32 @@ +#[cfg(test)] +mod index_test { + + use serde_json::json; + + use crate::database::{index::Index, table::Table}; + + #[test] + fn should_create_string_index_and_find_two_results() { + let indexes = hashmap!["race" => Index::new("race".to_string(), Some(false), Some(false))]; + let mut table: Table = Table::new("Cats", "id", Some(indexes)); + let _ = table.insert_row(row!["id" => 1, "name" => "Ozzy", "race" => "cat"]); + _ = table.insert_row(row!["id" => 2, "name" => "Simon", "race" => "cat"]); + _ = table.insert_row(row!["id" => 3, "name" => "Gosa", "race" => "dog"]); + + let result = table.get_pks_by_index("race".to_string(), json!("cat")); + + assert_eq!(result.is_ok(), true); + + let ids = result.unwrap(); + assert_eq!(ids, vec![1, 2]); + + let rows = table.find_by_pks(ids.clone()); + + assert_eq!(rows.len(), 2); + for row in rows { + assert_eq!(row.is_ok(), true); + let pretty_print = serde_json::to_string_pretty(row.unwrap()); + println!("Found Row: {}", pretty_print.unwrap()); + } + } +} diff --git a/src/tests/mod.rs b/src/tests/mod.rs index 876ff65..5e58ebc 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -1 +1,2 @@ +mod index_test; mod table_test; diff --git a/src/tests/table_test.rs b/src/tests/table_test.rs index 2081886..f05a0be 100644 --- a/src/tests/table_test.rs +++ b/src/tests/table_test.rs @@ -1,20 +1,20 @@ #[cfg(test)] -mod tests { +mod table_test { - use crate::database::{row::Row, table::Table}; + use crate::database::table::Table; #[test] fn should_find_two_rows() { - let mut table: Table = Table::new("Cats", "id"); - table.insert_row(Row::new(hashmapJson!["id" => 1, "name" => "Ozzy"])); - table.insert_row(Row::new(hashmapJson!["id" => 2, "name" => "Simon"])); + let mut table: Table = Table::new("Cats", "id", None); + let _ = table.insert_row(row!["id" => 1, "name" => "Ozzy"]); + _ = table.insert_row(row!["id" => 2, "name" => "Simon"]); - let row = table.find_by_pk(&1u64); + let row = table.find_by_pk(1u64); assert_eq!(row.is_err(), false); let pretty_print = serde_json::to_string_pretty(row.unwrap()); println!("Found Row: {}", pretty_print.unwrap()); - let row = table.find_by_pk(&2u64); + let row = table.find_by_pk(2u64); let pretty_print = serde_json::to_string_pretty(row.unwrap()); println!("Found Row: {}", pretty_print.unwrap()); @@ -22,11 +22,11 @@ mod tests { #[test] fn should_fail_to_find_row() { - let mut table: Table = Table::new("Cats", "id"); - table.insert_row(Row::new(hashmapJson!["id" => 1, "name" => "Ozzy"])); - table.insert_row(Row::new(hashmapJson!["id" => 2, "name" => "Simon"])); + let mut table: Table = Table::new("Cats", "id", None); + let _ = table.insert_row(row!["id" => 1, "name" => "Ozzy"]); + _ = table.insert_row(row!["id" => 2, "name" => "Simon"]); - let row = table.find_by_pk(&3u64); + let row = table.find_by_pk(3u64); assert_eq!(row.is_err(), true); } }