1use crate::store::GraphStore;
4use std::collections::HashMap;
5
6#[derive(Debug, Clone)]
8pub struct GraphQuery {
9 patterns: Vec<QueryPattern>,
10}
11
12#[derive(Debug, Clone)]
13pub struct QueryPattern {
14 pub subject: PatternValue,
15 pub predicate: PatternValue,
16 pub object: PatternValue,
17}
18
19#[derive(Debug, Clone)]
20pub enum PatternValue {
21 Variable(String),
22 Constant(String),
23}
24
25impl GraphQuery {
26 pub fn new() -> Self {
27 Self {
28 patterns: Vec::new(),
29 }
30 }
31
32 pub fn pattern_count(&self) -> usize {
34 self.patterns.len()
35 }
36
37 pub fn where_clause(mut self, subject: PatternValue, predicate: PatternValue, object: PatternValue) -> Self {
38 self.patterns.push(QueryPattern {
39 subject,
40 predicate,
41 object,
42 });
43 self
44 }
45
46 pub fn execute(&self, store: &GraphStore) -> Vec<HashMap<String, String>> {
48 let mut results = Vec::new();
49 self.execute_recursive(store, 0, &mut HashMap::new(), &mut results);
50 results
51 }
52
53 fn execute_recursive(
54 &self,
55 store: &GraphStore,
56 pattern_index: usize,
57 bindings: &mut HashMap<String, String>,
58 results: &mut Vec<HashMap<String, String>>,
59 ) {
60 if pattern_index >= self.patterns.len() {
61 results.push(bindings.clone());
63 return;
64 }
65
66 let pattern = &self.patterns[pattern_index];
67 let matches = self.find_matches(store, pattern, bindings);
68
69 for binding_update in matches {
70 let mut new_bindings = bindings.clone();
72 for (var, value) in binding_update {
73 new_bindings.insert(var, value);
74 }
75
76 self.execute_recursive(store, pattern_index + 1, &mut new_bindings, results);
78 }
79 }
80
81 fn find_matches(
82 &self,
83 store: &GraphStore,
84 pattern: &QueryPattern,
85 current_bindings: &HashMap<String, String>,
86 ) -> Vec<HashMap<String, String>> {
87 let subject = self.resolve_pattern_value(&pattern.subject, current_bindings);
88 let predicate = self.resolve_pattern_value(&pattern.predicate, current_bindings);
89 let object = self.resolve_pattern_value(&pattern.object, current_bindings);
90
91 let matches = store.find_triples(subject.as_deref(), predicate.as_deref(), object.as_deref());
92
93 let mut results = Vec::new();
94
95 for triple in matches {
96 let mut binding = HashMap::new();
97
98 if let PatternValue::Variable(var) = &pattern.subject {
100 if !current_bindings.contains_key(var) {
101 binding.insert(var.clone(), triple.subject.clone());
102 }
103 }
104 if let PatternValue::Variable(var) = &pattern.predicate {
105 if !current_bindings.contains_key(var) {
106 binding.insert(var.clone(), triple.predicate.clone());
107 }
108 }
109 if let PatternValue::Variable(var) = &pattern.object {
110 if !current_bindings.contains_key(var) {
111 binding.insert(var.clone(), triple.object.clone());
112 }
113 }
114
115 let mut consistent = true;
117 for (var, value) in &binding {
118 if let Some(existing) = current_bindings.get(var) {
119 if existing != value {
120 consistent = false;
121 break;
122 }
123 }
124 }
125
126 if consistent {
127 results.push(binding);
128 }
129 }
130
131 results
132 }
133
134 fn resolve_pattern_value(&self, value: &PatternValue, bindings: &HashMap<String, String>) -> Option<String> {
135 match value {
136 PatternValue::Constant(s) => Some(s.clone()),
137 PatternValue::Variable(var) => bindings.get(var).cloned(),
138 }
139 }
140}
141
142pub fn var(name: &str) -> PatternValue {
144 PatternValue::Variable(name.to_string())
145}
146
147pub fn const_val(value: &str) -> PatternValue {
148 PatternValue::Constant(value.to_string())
149}