oxirs_chat/rag/
query_processing.rs1use super::*;
6
7pub struct QueryProcessor;
9
10impl Default for QueryProcessor {
11 fn default() -> Self {
12 Self::new()
13 }
14}
15
16impl QueryProcessor {
17 pub fn new() -> Self {
18 Self
19 }
20
21 pub async fn extract_constraints(
23 &self,
24 query: &str,
25 entities: &[ExtractedEntity],
26 ) -> Result<Vec<QueryConstraint>> {
27 let mut constraints = Vec::new();
28 let query_lower = query.to_lowercase();
29
30 let temporal_patterns = [
32 (
33 r"(?:in|during|from|since|before|after)\s+(\d{4})",
34 ConstraintType::Temporal,
35 "year",
36 ),
37 (
38 r"(?:today|yesterday|tomorrow|now|recent)",
39 ConstraintType::Temporal,
40 "relative_time",
41 ),
42 ];
43
44 for (pattern, constraint_type, operator) in temporal_patterns {
45 let regex = Regex::new(pattern)?;
46 for cap in regex.captures_iter(&query_lower) {
47 if let Some(value) = cap.get(1) {
48 constraints.push(QueryConstraint {
49 constraint_type,
50 value: value.as_str().to_string(),
51 operator: operator.to_string(),
52 });
53 } else if cap.get(0).is_some() {
54 constraints.push(QueryConstraint {
55 constraint_type,
56 value: cap.get(0).unwrap().as_str().to_string(),
57 operator: operator.to_string(),
58 });
59 }
60 }
61 }
62
63 if query_lower.contains("type")
65 || query_lower.contains("kind")
66 || query_lower.contains("class")
67 {
68 constraints.push(QueryConstraint {
69 constraint_type: ConstraintType::Type,
70 value: "type_constraint".to_string(),
71 operator: "equals".to_string(),
72 });
73 }
74
75 let value_patterns = [
77 (r"(?:greater than|more than|>\s*)(\d+)", "greater_than"),
78 (r"(?:less than|fewer than|<\s*)(\d+)", "less_than"),
79 (r"(?:equals?|is|=\s*)(\d+)", "equals"),
80 ];
81
82 for (pattern, operator) in value_patterns {
83 let regex = Regex::new(pattern)?;
84 for cap in regex.captures_iter(&query_lower) {
85 if let Some(value) = cap.get(1) {
86 constraints.push(QueryConstraint {
87 constraint_type: ConstraintType::Value,
88 value: value.as_str().to_string(),
89 operator: operator.to_string(),
90 });
91 }
92 }
93 }
94
95 for entity in entities {
97 match entity.entity_type {
98 EntityType::Person => {
99 constraints.push(QueryConstraint {
100 constraint_type: ConstraintType::Entity,
101 value: entity.text.clone(),
102 operator: "person_filter".to_string(),
103 });
104 }
105 EntityType::Location => {
106 constraints.push(QueryConstraint {
107 constraint_type: ConstraintType::Spatial,
108 value: entity.text.clone(),
109 operator: "location_filter".to_string(),
110 });
111 }
112 _ => {}
113 }
114 }
115
116 debug!("Extracted {} constraints from query", constraints.len());
117 Ok(constraints)
118 }
119
120 pub fn analyze_query_intent(&self, query: &str) -> QueryIntent {
122 let query_lower = query.to_lowercase();
123
124 if query_lower.contains("how many") || query_lower.contains("count") {
125 QueryIntent::Counting
126 } else if query_lower.contains("what is") || query_lower.contains("define") {
127 QueryIntent::Definition
128 } else if query_lower.contains("compare") || query_lower.contains("difference") {
129 QueryIntent::Comparison
130 } else if query_lower.contains("list") || query_lower.contains("show all") {
131 QueryIntent::Listing
132 } else if query_lower.contains("why") || query_lower.contains("because") {
133 QueryIntent::Explanation
134 } else {
135 QueryIntent::General
136 }
137 }
138
139 pub fn calculate_query_complexity(&self, query: &str) -> f64 {
141 let word_count = query.split_whitespace().count();
142 let unique_words = query.split_whitespace().collect::<HashSet<_>>().len();
143 let question_words = ["what", "how", "why", "when", "where", "who", "which"];
144 let query_lower = query.to_lowercase();
145
146 let question_word_count = question_words
147 .iter()
148 .filter(|word| query_lower.contains(*word))
149 .count();
150
151 let complexity = (word_count as f64 * 0.05)
152 + (unique_words as f64 * 0.1)
153 + (question_word_count as f64 * 0.2);
154
155 complexity.min(1.0)
156 }
157}
158
159#[derive(Debug, Clone)]
161pub struct QueryConstraint {
162 pub constraint_type: ConstraintType,
163 pub value: String,
164 pub operator: String,
165}
166
167#[derive(Debug, Clone, Copy)]
169pub enum ConstraintType {
170 Temporal,
171 Spatial,
172 Type,
173 Value,
174 Entity,
175 Relationship,
176}
177
178#[derive(Debug, Clone, PartialEq, Hash, serde::Serialize, serde::Deserialize)]
180pub enum QueryIntent {
181 Definition,
182 Comparison,
183 Counting,
184 Listing,
185 Explanation,
186 General,
187}
188
189use super::graph_traversal::{EntityType, ExtractedEntity};