velesdb_core/velesql/ast/
mod.rs1mod aggregation;
6pub(crate) mod condition;
7mod dml;
8mod fusion;
9mod join;
10mod select;
11mod train;
12mod values;
13mod with_clause;
14
15use serde::{Deserialize, Serialize};
16
17pub use aggregation::{
19 AggregateArg, AggregateFunction, AggregateType, GroupByClause, HavingClause, HavingCondition,
20 LogicalOp,
21};
22pub use condition::{
23 BetweenCondition, CompareOp, Comparison, Condition, GraphMatchPredicate, InCondition,
24 IsNullCondition, LikeCondition, MatchCondition, SimilarityCondition, SparseVectorExpr,
25 SparseVectorSearch, VectorFusedSearch, VectorSearch,
26};
27pub use dml::{DmlStatement, InsertStatement, UpdateAssignment, UpdateStatement};
28pub use fusion::{FusionClause, FusionConfig, FusionStrategyType};
29pub use join::{ColumnRef, JoinClause, JoinCondition, JoinType};
30pub use select::{
31 Column, DistinctMode, OrderByExpr, SelectColumns, SelectOrderBy, SelectStatement,
32 SimilarityOrderBy,
33};
34pub use train::TrainStatement;
35pub use values::{
36 CorrelatedColumn, IntervalUnit, IntervalValue, Subquery, TemporalExpr, Value, VectorExpr,
37};
38pub use with_clause::{QuantizationMode, WithClause, WithOption, WithValue};
39
40#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
42pub struct Query {
43 pub select: SelectStatement,
45 #[serde(default)]
47 pub compound: Option<CompoundQuery>,
48 #[serde(default)]
50 pub match_clause: Option<crate::velesql::MatchClause>,
51 #[serde(default)]
53 pub dml: Option<DmlStatement>,
54 #[serde(default, skip_serializing_if = "Option::is_none")]
56 pub train: Option<TrainStatement>,
57}
58
59impl Query {
60 #[must_use]
62 pub fn is_match_query(&self) -> bool {
63 self.match_clause.is_some()
64 }
65
66 #[must_use]
68 pub fn is_select_query(&self) -> bool {
69 self.match_clause.is_none() && self.dml.is_none() && self.train.is_none()
70 }
71
72 #[must_use]
74 pub fn is_dml_query(&self) -> bool {
75 self.dml.is_some()
76 }
77
78 #[must_use]
80 pub fn is_train(&self) -> bool {
81 self.train.is_some()
82 }
83
84 #[must_use]
86 pub fn new_select(select: SelectStatement) -> Self {
87 Self {
88 select,
89 compound: None,
90 match_clause: None,
91 dml: None,
92 train: None,
93 }
94 }
95
96 #[must_use]
98 pub fn new_match(match_clause: crate::velesql::MatchClause) -> Self {
99 let select = SelectStatement {
100 distinct: DistinctMode::None,
101 columns: SelectColumns::All,
102 from: String::new(),
103 from_alias: Vec::new(),
104 joins: Vec::new(),
105 where_clause: match_clause.where_clause.clone(),
106 order_by: None,
107 limit: match_clause.return_clause.limit,
108 offset: None,
109 with_clause: None,
110 group_by: None,
111 having: None,
112 fusion_clause: None,
113 };
114 Self {
115 select,
116 compound: None,
117 match_clause: Some(match_clause),
118 dml: None,
119 train: None,
120 }
121 }
122
123 #[must_use]
125 pub fn new_dml(dml: DmlStatement) -> Self {
126 let select = SelectStatement {
127 distinct: DistinctMode::None,
128 columns: SelectColumns::All,
129 from: String::new(),
130 from_alias: Vec::new(),
131 joins: Vec::new(),
132 where_clause: None,
133 order_by: None,
134 limit: None,
135 offset: None,
136 with_clause: None,
137 group_by: None,
138 having: None,
139 fusion_clause: None,
140 };
141 Self {
142 select,
143 compound: None,
144 match_clause: None,
145 dml: Some(dml),
146 train: None,
147 }
148 }
149
150 #[must_use]
152 pub fn new_train(train: TrainStatement) -> Self {
153 let select = SelectStatement {
154 distinct: DistinctMode::None,
155 columns: SelectColumns::All,
156 from: String::new(),
157 from_alias: Vec::new(),
158 joins: Vec::new(),
159 where_clause: None,
160 order_by: None,
161 limit: None,
162 offset: None,
163 with_clause: None,
164 group_by: None,
165 having: None,
166 fusion_clause: None,
167 };
168 Self {
169 select,
170 compound: None,
171 match_clause: None,
172 dml: None,
173 train: Some(train),
174 }
175 }
176}
177
178#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
180pub enum SetOperator {
181 Union,
183 UnionAll,
185 Intersect,
187 Except,
189}
190
191#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
193pub struct CompoundQuery {
194 pub operator: SetOperator,
196 pub right: Box<SelectStatement>,
198}
199
200#[cfg(test)]
201mod tests {
202 use super::*;
203
204 #[test]
205 fn test_with_clause_new() {
206 let clause = WithClause::new();
207 assert!(clause.options.is_empty());
208 }
209
210 #[test]
211 fn test_with_clause_with_option() {
212 let clause = WithClause::new()
213 .with_option("mode", WithValue::String("accurate".to_string()))
214 .with_option("ef_search", WithValue::Integer(512));
215 assert_eq!(clause.options.len(), 2);
216 }
217
218 #[test]
219 fn test_with_clause_get() {
220 let clause = WithClause::new().with_option("mode", WithValue::String("fast".to_string()));
221 assert!(clause.get("mode").is_some());
222 assert!(clause.get("MODE").is_some());
223 assert!(clause.get("unknown").is_none());
224 }
225
226 #[test]
227 fn test_with_clause_get_mode() {
228 let clause =
229 WithClause::new().with_option("mode", WithValue::String("accurate".to_string()));
230 assert_eq!(clause.get_mode(), Some("accurate"));
231 }
232
233 #[test]
234 fn test_with_value_as_str() {
235 let v = WithValue::String("test".to_string());
236 assert_eq!(v.as_str(), Some("test"));
237 }
238
239 #[test]
240 fn test_with_value_as_integer() {
241 let v = WithValue::Integer(100);
242 assert_eq!(v.as_integer(), Some(100));
243 }
244
245 #[test]
246 fn test_with_value_as_float() {
247 let v = WithValue::Float(1.234);
248 assert!((v.as_float().unwrap() - 1.234).abs() < 1e-5);
249 }
250
251 #[test]
252 fn test_interval_to_seconds() {
253 assert_eq!(
254 IntervalValue {
255 magnitude: 30,
256 unit: IntervalUnit::Seconds
257 }
258 .to_seconds(),
259 30
260 );
261 assert_eq!(
262 IntervalValue {
263 magnitude: 1,
264 unit: IntervalUnit::Days
265 }
266 .to_seconds(),
267 86400
268 );
269 }
270
271 #[test]
272 fn test_temporal_now() {
273 let expr = TemporalExpr::Now;
274 let epoch = expr.to_epoch_seconds();
275 assert!(epoch > 1_577_836_800);
276 }
277
278 #[test]
279 fn test_value_from_i64() {
280 let v: Value = 42i64.into();
281 assert_eq!(v, Value::Integer(42));
282 }
283
284 #[test]
285 fn test_fusion_config_default() {
286 let config = FusionConfig::default();
287 assert_eq!(config.strategy, "rrf");
288 }
289
290 #[test]
291 fn test_fusion_config_rrf() {
292 let config = FusionConfig::rrf();
293 assert_eq!(config.strategy, "rrf");
294 assert!((config.params.get("k").unwrap() - 60.0).abs() < 1e-5);
295 }
296
297 #[test]
298 fn test_fusion_clause_default() {
299 let clause = FusionClause::default();
300 assert_eq!(clause.strategy, FusionStrategyType::Rrf);
301 assert_eq!(clause.k, Some(60));
302 }
303
304 #[test]
305 fn test_group_by_clause_default() {
306 let clause = GroupByClause::default();
307 assert!(clause.columns.is_empty());
308 }
309
310 #[test]
311 fn test_having_clause_default() {
312 let clause = HavingClause::default();
313 assert!(clause.conditions.is_empty());
314 }
315}