feat: index feature

This commit is contained in:
Elias Renman
2023-05-07 15:31:36 +02:00
parent 8ea347bc98
commit a8a527e037
8 changed files with 203 additions and 51 deletions

View File

@@ -1,9 +1,78 @@
use super::row::Row;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::collections::HashMap;
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct Index { pub struct Index {
pk: u64, desc_sort: bool,
key: String, key: String,
asd_sort: bool,
unique: bool, unique: bool,
index_rows: HashMap<String, Vec<u64>>,
}
impl Index {
pub fn new(key: String, desc_sort: Option<bool>, unique: Option<bool>) -> 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<u64>> {
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,
} }

View File

@@ -39,14 +39,14 @@ impl Database {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::database::{row::Row, table::Table, Database}; use crate::database::{table::Table, Database};
use std::{fs, path::Path}; use std::{fs, path::Path};
#[test] #[test]
fn should_write_and_read_to_file() { fn should_write_and_read_to_file() {
let mut table: Table = Table::new("Cats", "id"); let mut table: Table = Table::new("Cats", "id", None);
table.insert_row(Row::new(hashmapJson!["id" => 1, "name" => "Ozzy"])); let _ = table.insert_row(row!["id" => 1, "name" => "Ozzy"]);
table.insert_row(Row::new(hashmapJson!["id" => 2, "name" => "Simon"])); _ = table.insert_row(row!["id" => 2, "name" => "Simon"]);
let database = Database::new(hashmap!["Cats" => table]); let database = Database::new(hashmap!["Cats" => table]);
database.to_file("./db.json"); database.to_file("./db.json");
@@ -63,7 +63,7 @@ mod tests {
panic!("Unable to find table"); 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); assert_eq!(row.is_ok(), true);
// Cleanup file // Cleanup file

View File

@@ -1,14 +1,4 @@
use serde::{Deserialize, Serialize};
use serde_json::Value; use serde_json::Value;
use std::collections::HashMap; use std::collections::HashMap;
#[derive(Serialize, Deserialize)] pub type Row = HashMap<String, Value>;
pub struct Row {
pub columns: HashMap<String, Value>,
}
impl Row {
pub fn new(cols: HashMap<String, Value>) -> Row {
Row { columns: cols }
}
}

View File

@@ -1,51 +1,103 @@
use super::{index::Index, row::Row}; use super::{index::Index, row::Row};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::{collections::HashMap, fs}; use serde_json::Value;
use std::collections::{hash_map::Entry, HashMap};
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct Table { pub struct Table {
indexes: Vec<Index>, indexes: HashMap<String, Index>,
rows: HashMap<u64, Row>, rows: HashMap<u64, Row>,
name: String, name: String,
pk_key: String, pk: String,
} }
impl Table { impl Table {
pub fn new(name: &str, pk_key: &str) -> Table { pub fn new(name: &str, pk: &str, indexes: Option<HashMap<String, Index>>) -> Table {
Table { Table {
indexes: vec![], indexes: indexes.unwrap_or(HashMap::new()),
rows: HashMap::new(), rows: HashMap::new(),
name: name.to_string(), name: name.to_string(),
pk_key: pk_key.to_string(), pk: pk.to_string(),
} }
} }
pub fn create_index(&mut self, index: Index) { pub fn create_index(&mut self, key: String, mut index: Index) {
// TODO: Add row indexes to the index itself before pushing to the table for ele in self.rows.clone() {
self.indexes.push(index) let _ = index.insert_row(self.pk.clone(), ele.1.clone());
}
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!");
} }
Ok(row.unwrap().clone()) self.indexes.insert(key, index);
} }
pub fn insert_row(&mut self, row: Row) { pub fn insert_row(&mut self, row: Row) -> Result<(), &str> {
let key_option = row.columns.get(&self.pk_key); let key_option = row.get(&self.pk);
if key_option.is_none() { if key_option.is_none() {
panic!("Primary key not found on row to insert!"); 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"); 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<u64>) -> Vec<Result<&Row, String>> {
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<Vec<u64>, 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());
} }
} }

View File

@@ -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 { macro_rules! hashmap {
($( $key: expr => $val: expr ),*) => {{ ($( $key: expr => $val: expr ),*) => {{
let mut map = ::std::collections::HashMap::new(); let mut map = ::std::collections::HashMap::new();
$( map.insert($key.to_string(), $val ))*; $( map.insert($key.to_string(), $val );)*
map map
}} }}
} }

32
src/tests/index_test.rs Normal file
View File

@@ -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());
}
}
}

View File

@@ -1 +1,2 @@
mod index_test;
mod table_test; mod table_test;

View File

@@ -1,20 +1,20 @@
#[cfg(test)] #[cfg(test)]
mod tests { mod table_test {
use crate::database::{row::Row, table::Table}; use crate::database::table::Table;
#[test] #[test]
fn should_find_two_rows() { fn should_find_two_rows() {
let mut table: Table = Table::new("Cats", "id"); let mut table: Table = Table::new("Cats", "id", None);
table.insert_row(Row::new(hashmapJson!["id" => 1, "name" => "Ozzy"])); let _ = table.insert_row(row!["id" => 1, "name" => "Ozzy"]);
table.insert_row(Row::new(hashmapJson!["id" => 2, "name" => "Simon"])); _ = 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); assert_eq!(row.is_err(), false);
let pretty_print = serde_json::to_string_pretty(row.unwrap()); let pretty_print = serde_json::to_string_pretty(row.unwrap());
println!("Found Row: {}", pretty_print.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()); let pretty_print = serde_json::to_string_pretty(row.unwrap());
println!("Found Row: {}", pretty_print.unwrap()); println!("Found Row: {}", pretty_print.unwrap());
@@ -22,11 +22,11 @@ mod tests {
#[test] #[test]
fn should_fail_to_find_row() { fn should_fail_to_find_row() {
let mut table: Table = Table::new("Cats", "id"); let mut table: Table = Table::new("Cats", "id", None);
table.insert_row(Row::new(hashmapJson!["id" => 1, "name" => "Ozzy"])); let _ = table.insert_row(row!["id" => 1, "name" => "Ozzy"]);
table.insert_row(Row::new(hashmapJson!["id" => 2, "name" => "Simon"])); _ = 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); assert_eq!(row.is_err(), true);
} }
} }