use super::goal::Goal;
use super::unification::Bindings;
#[derive(Debug, Clone)]
pub struct NestedQuery {
pub outer_goal: Goal,
pub subquery: Box<Query>,
pub shared_variables: Vec<String>,
}
#[derive(Debug, Clone)]
pub struct Query {
pub goals: Vec<Goal>,
pub nested: Vec<NestedQuery>,
pub pattern: String,
}
impl Query {
pub fn new(pattern: String) -> Self {
Self {
goals: Vec::new(),
nested: Vec::new(),
pattern,
}
}
pub fn add_goal(&mut self, goal: Goal) {
self.goals.push(goal);
}
pub fn add_nested(&mut self, nested: NestedQuery) {
self.nested.push(nested);
}
pub fn has_nested(&self) -> bool {
!self.nested.is_empty()
}
pub fn variables(&self) -> Vec<String> {
let mut vars = Vec::new();
for goal in &self.goals {
let pattern_vars = Self::extract_variables(&goal.pattern);
for var in pattern_vars {
if !vars.contains(&var) {
vars.push(var);
}
}
}
vars
}
fn extract_variables(pattern: &str) -> Vec<String> {
let mut vars = Vec::new();
let chars: Vec<char> = pattern.chars().collect();
let mut i = 0;
while i < chars.len() {
if chars[i] == '?' {
let mut var = String::from("?");
i += 1;
while i < chars.len() && (chars[i].is_alphanumeric() || chars[i] == '_') {
var.push(chars[i]);
i += 1;
}
if !vars.contains(&var) {
vars.push(var);
}
} else {
i += 1;
}
}
vars
}
}
impl NestedQuery {
pub fn new(outer_goal: Goal, subquery: Query) -> Self {
let outer_vars = Query::extract_variables(&outer_goal.pattern);
let subquery_vars = subquery.variables();
let shared_variables: Vec<String> = outer_vars
.iter()
.filter(|v| subquery_vars.contains(v))
.cloned()
.collect();
Self {
outer_goal,
subquery: Box::new(subquery),
shared_variables,
}
}
pub fn has_shared_variables(&self) -> bool {
!self.shared_variables.is_empty()
}
}
#[derive(Debug, Clone)]
pub struct NestedQueryResult {
pub subquery_solutions: Vec<Bindings>,
pub final_solutions: Vec<Bindings>,
pub success: bool,
pub subquery_count: usize,
}
impl NestedQueryResult {
pub fn new() -> Self {
Self {
subquery_solutions: Vec::new(),
final_solutions: Vec::new(),
success: false,
subquery_count: 0,
}
}
pub fn success(subquery_solutions: Vec<Bindings>, final_solutions: Vec<Bindings>) -> Self {
Self {
success: !final_solutions.is_empty(),
subquery_count: subquery_solutions.len(),
subquery_solutions,
final_solutions,
}
}
pub fn failure() -> Self {
Self {
subquery_solutions: Vec::new(),
final_solutions: Vec::new(),
success: false,
subquery_count: 0,
}
}
}
impl Default for NestedQueryResult {
fn default() -> Self {
Self::new()
}
}
pub struct NestedQueryParser;
impl NestedQueryParser {
pub fn parse(query_str: &str) -> Query {
let mut query = Query::new(query_str.to_string());
if let Some(where_idx) = query_str.find(" WHERE ") {
let conditions = &query_str[where_idx + 7..].trim();
let goals = Self::parse_conditions(conditions);
for goal in goals {
query.add_goal(goal);
}
}
query
}
fn parse_conditions(conditions: &str) -> Vec<Goal> {
let mut goals = Vec::new();
for condition in conditions.split(" AND ") {
let condition = condition.trim();
if condition.contains(" WHERE ") {
continue;
}
if !condition.is_empty() && !condition.starts_with('(') {
goals.push(Goal::new(condition.to_string()));
}
}
goals
}
pub fn has_nested(query_str: &str) -> bool {
let mut paren_depth = 0;
let mut in_parens = false;
let chars: Vec<char> = query_str.chars().collect();
for i in 0..chars.len() {
match chars[i] {
'(' => {
paren_depth += 1;
in_parens = true;
}
')' => {
paren_depth -= 1;
if paren_depth == 0 {
in_parens = false;
}
}
'W' if in_parens && paren_depth > 0 => {
if i + 5 < chars.len() {
let substr: String = chars[i..i + 5].iter().collect();
if substr == "WHERE" {
return true;
}
}
}
_ => {}
}
}
false
}
}
pub struct NestedQueryEvaluator;
impl NestedQueryEvaluator {
pub fn evaluate(_nested: &NestedQuery, _initial_bindings: &Bindings) -> NestedQueryResult {
NestedQueryResult::new()
}
#[allow(dead_code)]
fn merge_bindings(
outer_bindings: &Bindings,
subquery_bindings: &Bindings,
shared_vars: &[String],
) -> Bindings {
let mut merged = outer_bindings.clone();
for var in shared_vars {
if let Some(value) = subquery_bindings.get(var) {
let _ = merged.bind(var.clone(), value.clone());
}
}
merged
}
}
#[derive(Debug, Clone, Default)]
pub struct NestedQueryStats {
pub total_nested: usize,
pub total_subquery_evals: usize,
pub max_depth: usize,
pub avg_subquery_solutions: f64,
}
impl NestedQueryStats {
pub fn new() -> Self {
Self::default()
}
pub fn record_evaluation(&mut self, depth: usize, subquery_solutions: usize) {
self.total_nested += 1;
self.total_subquery_evals += subquery_solutions;
self.max_depth = self.max_depth.max(depth);
self.avg_subquery_solutions = self.total_subquery_evals as f64 / self.total_nested as f64;
}
pub fn summary(&self) -> String {
format!(
"Nested queries: {} | Subquery evals: {} | Max depth: {} | Avg solutions: {:.2}",
self.total_nested,
self.total_subquery_evals,
self.max_depth,
self.avg_subquery_solutions
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_query_creation() {
let query = Query::new("test(?x)".to_string());
assert_eq!(query.pattern, "test(?x)");
assert!(query.goals.is_empty());
assert!(!query.has_nested());
}
#[test]
fn test_query_add_goal() {
let mut query = Query::new("test".to_string());
let goal = Goal::new("parent(?x, ?y)".to_string());
query.add_goal(goal);
assert_eq!(query.goals.len(), 1);
}
#[test]
fn test_query_variables() {
let mut query = Query::new("test".to_string());
let goal1 = Goal::new("parent(?x, ?y)".to_string());
let goal2 = Goal::new("age(?x, ?age)".to_string());
query.add_goal(goal1);
query.add_goal(goal2);
let vars = query.variables();
assert!(vars.contains(&"?x".to_string()));
assert!(vars.contains(&"?y".to_string()));
assert!(vars.contains(&"?age".to_string()));
}
#[test]
fn test_nested_query_creation() {
let outer_goal = Goal::new("grandparent(?x, ?z)".to_string());
let mut subquery = Query::new("parent(?y, ?z)".to_string());
let sub_goal = Goal::new("parent(?y, ?z)".to_string());
subquery.add_goal(sub_goal);
let nested = NestedQuery::new(outer_goal, subquery);
assert!(!nested.subquery.goals.is_empty());
}
#[test]
fn test_nested_query_shared_variables() {
let outer_goal = Goal::new("grandparent(?x, ?z)".to_string());
let mut subquery = Query::new("parent(?y, ?z)".to_string());
let sub_goal = Goal::new("parent(?y, ?z)".to_string());
subquery.add_goal(sub_goal);
let nested = NestedQuery::new(outer_goal, subquery);
assert!(nested.has_shared_variables());
assert!(nested.shared_variables.contains(&"?z".to_string()));
}
#[test]
fn test_nested_query_result() {
let result = NestedQueryResult::new();
assert!(!result.success);
assert_eq!(result.subquery_count, 0);
let success = NestedQueryResult::success(vec![], vec![]);
assert!(!success.success); }
#[test]
fn test_nested_query_parser_has_nested() {
assert!(NestedQueryParser::has_nested(
"grandparent(?x, ?z) WHERE parent(?x, ?y) AND (parent(?y, ?z) WHERE child(?z, ?y))"
));
assert!(!NestedQueryParser::has_nested(
"parent(?x, ?y) WHERE person(?x) AND person(?y)"
));
}
#[test]
fn test_nested_query_stats() {
let mut stats = NestedQueryStats::new();
stats.record_evaluation(1, 5);
stats.record_evaluation(2, 10);
assert_eq!(stats.total_nested, 2);
assert_eq!(stats.total_subquery_evals, 15);
assert_eq!(stats.max_depth, 2);
assert_eq!(stats.avg_subquery_solutions, 7.5);
}
#[test]
fn test_parser_simple_query() {
let query = NestedQueryParser::parse("parent(?x, ?y) WHERE person(?x) AND person(?y)");
assert!(!query.goals.is_empty());
}
}