more tests progress
This commit is contained in:
@ -24,61 +24,105 @@ impl Queryer {
|
||||
stem_opt: Option<&str>,
|
||||
filters: Option<&serde_json::Value>,
|
||||
) -> crate::drop::Drop {
|
||||
let filters_map: Option<&serde_json::Map<String, serde_json::Value>> =
|
||||
filters.and_then(|f| f.as_object());
|
||||
let filters_map = filters.and_then(|f| f.as_object());
|
||||
|
||||
// Generate Permutation Cache Key: schema_id + sorted filter keys
|
||||
let mut filter_keys: Vec<String> = Vec::new();
|
||||
if let Some(fm) = filters_map {
|
||||
for key in fm.keys() {
|
||||
filter_keys.push(key.clone());
|
||||
// 1. Process filters into structured $op keys and linear values
|
||||
let (filter_keys, args) = match self.parse_filter_entries(filters_map) {
|
||||
Ok(res) => res,
|
||||
Err(msg) => {
|
||||
return crate::drop::Drop::with_errors(vec![crate::drop::Error {
|
||||
code: "FILTER_PARSE_FAILED".to_string(),
|
||||
message: msg,
|
||||
details: crate::drop::ErrorDetails {
|
||||
path: schema_id.to_string(),
|
||||
},
|
||||
}]);
|
||||
}
|
||||
}
|
||||
filter_keys.sort();
|
||||
};
|
||||
|
||||
let stem_key = stem_opt.unwrap_or("/");
|
||||
let cache_key = format!("{}(Stem:{}):{}", schema_id, stem_key, filter_keys.join(","));
|
||||
|
||||
let sql = if let Some(cached_sql) = self.cache.get(&cache_key) {
|
||||
cached_sql.value().clone()
|
||||
} else {
|
||||
// Compile the massive base SQL string
|
||||
let compiler = compiler::SqlCompiler::new(self.db.clone());
|
||||
match compiler.compile(schema_id, stem_opt, &filter_keys) {
|
||||
Ok(compiled_sql) => {
|
||||
self.cache.insert(cache_key.clone(), compiled_sql.clone());
|
||||
compiled_sql
|
||||
}
|
||||
Err(e) => {
|
||||
return crate::drop::Drop::with_errors(vec![crate::drop::Error {
|
||||
code: "QUERY_COMPILATION_FAILED".to_string(),
|
||||
message: e,
|
||||
details: crate::drop::ErrorDetails {
|
||||
path: schema_id.to_string(),
|
||||
},
|
||||
}]);
|
||||
}
|
||||
}
|
||||
// 2. Fetch from cache or compile
|
||||
let sql = match self.get_or_compile_sql(&cache_key, schema_id, stem_opt, &filter_keys) {
|
||||
Ok(sql) => sql,
|
||||
Err(drop) => return drop,
|
||||
};
|
||||
|
||||
// 2. Prepare the execution arguments from the filters
|
||||
let mut args: Vec<serde_json::Value> = Vec::new();
|
||||
// 3. Execute via Database Executor
|
||||
self.execute_sql(schema_id, &sql, &args)
|
||||
}
|
||||
|
||||
fn parse_filter_entries(
|
||||
&self,
|
||||
filters_map: Option<&serde_json::Map<String, serde_json::Value>>,
|
||||
) -> Result<(Vec<String>, Vec<serde_json::Value>), String> {
|
||||
let mut filter_entries: Vec<(String, serde_json::Value)> = Vec::new();
|
||||
if let Some(fm) = filters_map {
|
||||
for (_i, key) in filter_keys.iter().enumerate() {
|
||||
if let Some(val) = fm.get(key) {
|
||||
args.push(val.clone());
|
||||
for (key, val) in fm {
|
||||
if let Some(obj) = val.as_object() {
|
||||
for (op, op_val) in obj {
|
||||
if !op.starts_with('$') {
|
||||
return Err(format!("Filter operator must start with '$', got: {}", op));
|
||||
}
|
||||
filter_entries.push((format!("{}:{}", key, op), op_val.clone()));
|
||||
}
|
||||
} else {
|
||||
return Err(format!(
|
||||
"Filter for field '{}' must be an object with operators like $eq, $in, etc.",
|
||||
key
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
filter_entries.sort_by(|a, b| a.0.cmp(&b.0));
|
||||
|
||||
// 3. Execute via Database Executor
|
||||
match self.db.query(&sql, Some(&args)) {
|
||||
let filter_keys: Vec<String> = filter_entries.iter().map(|(k, _)| k.clone()).collect();
|
||||
let args: Vec<serde_json::Value> = filter_entries.into_iter().map(|(_, v)| v).collect();
|
||||
|
||||
Ok((filter_keys, args))
|
||||
}
|
||||
|
||||
fn get_or_compile_sql(
|
||||
&self,
|
||||
cache_key: &str,
|
||||
schema_id: &str,
|
||||
stem_opt: Option<&str>,
|
||||
filter_keys: &[String],
|
||||
) -> Result<String, crate::drop::Drop> {
|
||||
if let Some(cached_sql) = self.cache.get(cache_key) {
|
||||
return Ok(cached_sql.value().clone());
|
||||
}
|
||||
|
||||
let compiler = compiler::SqlCompiler::new(self.db.clone());
|
||||
match compiler.compile(schema_id, stem_opt, filter_keys) {
|
||||
Ok(compiled_sql) => {
|
||||
self
|
||||
.cache
|
||||
.insert(cache_key.to_string(), compiled_sql.clone());
|
||||
Ok(compiled_sql)
|
||||
}
|
||||
Err(e) => Err(crate::drop::Drop::with_errors(vec![crate::drop::Error {
|
||||
code: "QUERY_COMPILATION_FAILED".to_string(),
|
||||
message: e,
|
||||
details: crate::drop::ErrorDetails {
|
||||
path: schema_id.to_string(),
|
||||
},
|
||||
}])),
|
||||
}
|
||||
}
|
||||
|
||||
fn execute_sql(
|
||||
&self,
|
||||
schema_id: &str,
|
||||
sql: &str,
|
||||
args: &[serde_json::Value],
|
||||
) -> crate::drop::Drop {
|
||||
match self.db.query(sql, Some(args)) {
|
||||
Ok(serde_json::Value::Array(table)) => {
|
||||
if table.is_empty() {
|
||||
crate::drop::Drop::success_with_val(serde_json::Value::Null)
|
||||
} else {
|
||||
// We expect the query to return a single JSONB column, already unpacked from row[0]
|
||||
crate::drop::Drop::success_with_val(table.first().unwrap().clone())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user