diff --git a/src/database/query/select_processor.rs b/src/database/query/select_processor.rs index 696a90a..318b16e 100644 --- a/src/database/query/select_processor.rs +++ b/src/database/query/select_processor.rs @@ -1,7 +1,11 @@ use serde_json::{json, Map, Value}; -use crate::database::{relation::{OneToMany, OneToOne}, Database, row::Row, query::select_parser::SelectParser}; - +use crate::database::{ + query::select_parser::SelectParser, + relation::{OneToMany, OneToOne}, + row::Row, + Database, +}; pub struct SelectProcessor {} @@ -14,9 +18,8 @@ impl SelectProcessor { row: &Row, select: Vec<&str>, ) -> Map { - let asd = SelectParser::parse_selector_recursive(select); - println!("Debugging parsed selector {asd}"); - SelectProcessor::recursive_traverse_resolver(database, table_name, row, &asd) + let selector = SelectParser::parse_selector_recursive(select); + SelectProcessor::recursive_traverse_resolver(database, table_name, row, &selector) } fn recursive_traverse_resolver( @@ -28,32 +31,20 @@ impl SelectProcessor { let object = selector.as_object().unwrap(); let mut output: Map = Map::new(); for (key, value) in object.into_iter() { - if value.is_object() { - let relation_rows: Value = - SelectProcessor::resolve_relation(database, table_name, row, key); - - if relation_rows.is_array() { - let mut row_vec: Vec> = vec![]; - for row in relation_rows.as_array().unwrap() { - let asd = row.as_object().unwrap(); - let parsed_row = SelectProcessor::recursive_traverse_resolver( - database, table_name, &asd, &value, - ); - row_vec.push(parsed_row); - } - output.insert(key.to_owned(), json!(row_vec)); - } else { - let foreign_row = relation_rows.as_object().unwrap(); - let parsed_row = SelectProcessor::recursive_traverse_resolver( - database, table_name, foreign_row, &value, + match value { + Value::Object(_val) => { + output.insert( + key.to_owned(), + SelectProcessor::resolve_foreign_relation( + database, table_name, row, key, value, + ), ); - - output.insert(key.to_owned(), json!(parsed_row)); } - } else { - if value.as_str().unwrap() == "*" { - output = row.to_owned(); - } else { + Value::String(val) => { + if val == "*" { + output = row.to_owned(); + continue; + } let val = row.get(key); if val.is_none() { let pretty_json = serde_json::to_string_pretty(row).unwrap(); @@ -63,29 +54,60 @@ impl SelectProcessor { } output.insert(key.to_owned(), json!(val.unwrap())); } + _ => panic!("Value has invalid type"), } } return output; } + fn resolve_foreign_relation( + database: &Database, + table_name: &String, + row: &Row, + key: &String, + selector: &Value, + ) -> Value { + let (new_table_name,relation_row_value) = + SelectProcessor::resolve_relation(database, table_name, row, key); + + if relation_row_value.is_array() { + let mut row_vec: Vec> = vec![]; + for row in relation_row_value.as_array().unwrap() { + let asd = row.as_object().unwrap(); + let parsed_row = SelectProcessor::recursive_traverse_resolver( + database, &new_table_name, &asd, &selector, + ); + row_vec.push(parsed_row); + } + return json!(row_vec); + } + + let foreign_row = relation_row_value.as_object().unwrap(); + let parsed_row = SelectProcessor::recursive_traverse_resolver( + database, + table_name, + foreign_row, + &selector, + ); + return json!(parsed_row); + } + pub fn resolve_relation( database: &Database, table_name: &String, row: &Row, key: &String, - ) -> Value { + ) -> (String,Value) { let table = database.get_table(table_name.clone()).unwrap(); println!("Attempting to find relation for '{key}' in table: '{table_name}'"); - let value = row.get(key).unwrap(); let relation_name = match value.get("relation_name") { Some(val) => String::from(val.as_str().unwrap()), None => panic!("Value was not a valid relation"), }; let relation = table.get_relation(&relation_name).unwrap(); - let relation_variation = relation.variation.as_str(); - return match relation_variation { + let relation_value = match relation.variation.as_str() { "one_to_one" => json!(OneToOne::from_value(value.to_owned()) .get(table_name.clone(), database) .unwrap()), @@ -94,9 +116,7 @@ impl SelectProcessor { .unwrap()), other => panic!("Unsupported relationship type: {other}"), }; + (relation.table_name.to_owned(), relation_value) } - fn is_valid_key(key: &str) -> bool { - key.chars().all(|c| c.is_ascii_lowercase() || c == '_') - } } diff --git a/src/tests/select_processor_test.rs b/src/tests/select_processor_test.rs index c6e2ebe..3abc1f5 100644 --- a/src/tests/select_processor_test.rs +++ b/src/tests/select_processor_test.rs @@ -3,37 +3,54 @@ #[cfg(test)] mod select_processor_test { use crate::database::{ - relation::{OneToMany, Relation, OneToOne}, + query::select_processor::SelectProcessor, + relation::{OneToMany, OneToOne, Relation}, table::Table, - Database, query::select_processor::SelectProcessor, + Database, }; use assert_json_diff::assert_json_include; use serde_json::json; pub fn initialize() -> Database { - let cat_relations = - hashmap!["cat_food" => Relation::new("Food".to_string(), "one_to_many".to_string()), + let cat_relations = hashmap!["cat_food" => Relation::new("Food".to_string(), "one_to_many".to_string()), "cat_age_group" => Relation::new("AgeGroup".to_string(), "one_to_one".to_string())]; + let food_relations = hashmap!["food_ingredient" => Relation::new("Ingredient".to_string(), "one_to_many".to_string())]; let cat_table: Table = Table::new("Cats", "id", None, Some(cat_relations)); - let food_table: Table = Table::new("Food", "id", None, None); + let food_table: Table = Table::new("Food", "id", None, Some(food_relations)); + let ingredient_table: Table = Table::new("Ingredient", "id", None, None); let age_table: Table = Table::new("AgeGroup", "id", None, None); - let mut database: Database = Database::new(vec![cat_table, food_table,age_table]); + let mut database: Database = + Database::new(vec![cat_table, food_table, age_table, ingredient_table]); _ = database .get_table_mut("Food".to_string()) .unwrap() - .insert_row(row!["id" => 123, "name" => "Wet Feed"]); + .insert_row(row![ + "id" => 123, + "name" => "Wet Feed", + "ingridients" => OneToMany::new(vec![], "food_ingredient".to_string())]); _ = database .get_table_mut("Food".to_string()) .unwrap() - .insert_row(row!["id" => 12, "name" => "Dry Feed"]); + .insert_row(row![ + "id" => 12, + "name" => "Dry Feed", + "ingridients" => OneToMany::new(vec![33u64], "food_ingredient".to_string()) + ]); + _ = database + .get_table_mut("Ingredient".to_string()) + .unwrap() + .insert_row(row!["id" => 33, "name" => "Chicken Meat"]); _ = database .get_table_mut("AgeGroup".to_string()) .unwrap() .insert_row(row!["id" => 99, "age" => "Young"]); - _ = database.get_table_mut("Cats".to_string()).unwrap().insert_row(row![ + _ = database + .get_table_mut("Cats".to_string()) + .unwrap() + .insert_row(row![ "id" => 1, "name" => "Ozzy", "breed" => "mixed", @@ -42,6 +59,28 @@ mod select_processor_test { return database; } + + #[test] + fn should_succed_select_processor_two_level_resolved() { + let database = initialize(); + + let cat_table = database.get_table("Cats".to_string()).unwrap(); + let cat_1 = cat_table.find_by_pk(1u64).unwrap(); + let select = vec!["id", "name", "foods.*", "foods.ingridients.name"]; + let output = SelectProcessor::selector(&database, &cat_table.name, cat_1, select); + let pretty = serde_json::to_string_pretty(&output).unwrap(); + println!("Json output {pretty}"); + assert_json_include!( + actual: &output, + expected: + &row!["id" => 1, "name" =>"Ozzy", "foods" => + vec![ + row!["id" => 123, "name" => "Wet Feed", "ingridients" => json!([])], + row!["id" => 12, "name" => "Dry Feed", "ingridients" => vec![row!["name" => "Chicken Meat"]]] + ]] + ); + } + #[test] fn should_succed_select_processor_one_level_resolved() { let database = initialize(); @@ -61,22 +100,24 @@ mod select_processor_test { ]] ); } -#[test] - fn should_succed_select_processor_one_level_resolved_onetoone() { - let database = initialize(); - let cat_table = database.get_table("Cats".to_string()).unwrap(); - let cat_1 = cat_table.find_by_pk(1u64).unwrap(); - let select = vec!["id", "name", "group.age"]; - let output = SelectProcessor::selector(&database, &cat_table.name, cat_1, select); - assert_json_include!( - actual: &output, + #[test] + fn should_succed_select_processor_one_level_resolved_onetoone() { + let database = initialize(); + + let cat_table = database.get_table("Cats".to_string()).unwrap(); + let cat_1 = cat_table.find_by_pk(1u64).unwrap(); + let select = vec!["id", "name", "group.age"]; + let output = SelectProcessor::selector(&database, &cat_table.name, cat_1, select); + assert_json_include!( + actual: &output, expected: &row!["id" => 1, "name" =>"Ozzy", "group"=> row![ - "age" => "Young" - ]] + "age" => "Young" + ]] ); } + #[test] fn should_succed_select_processor_zero_level_resolved() { let database = initialize(); @@ -90,6 +131,7 @@ mod select_processor_test { expected: &row!["id" => 1, "name" =>"Ozzy", "breed" => "mixed"] ); } + #[test] fn should_succed_select_processor_zero_level_asterix_resolved() { let database = initialize();