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