1use serde::{Deserialize, Serialize};
6
7#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
9pub struct Query {
10 pub select: SelectStatement,
12}
13
14#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
16pub struct SelectStatement {
17 pub columns: SelectColumns,
19 pub from: String,
21 pub where_clause: Option<Condition>,
23 pub limit: Option<u64>,
25 pub offset: Option<u64>,
27 pub with_clause: Option<WithClause>,
29}
30
31#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
42pub struct WithClause {
43 pub options: Vec<WithOption>,
45}
46
47impl WithClause {
48 #[must_use]
50 pub fn new() -> Self {
51 Self::default()
52 }
53
54 #[must_use]
56 pub fn with_option(mut self, key: impl Into<String>, value: WithValue) -> Self {
57 self.options.push(WithOption {
58 key: key.into(),
59 value,
60 });
61 self
62 }
63
64 #[must_use]
66 pub fn get(&self, key: &str) -> Option<&WithValue> {
67 self.options
68 .iter()
69 .find(|opt| opt.key.eq_ignore_ascii_case(key))
70 .map(|opt| &opt.value)
71 }
72
73 #[must_use]
75 pub fn get_mode(&self) -> Option<&str> {
76 self.get("mode").and_then(|v| v.as_str())
77 }
78
79 #[must_use]
81 #[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
82 pub fn get_ef_search(&self) -> Option<usize> {
83 self.get("ef_search")
84 .and_then(WithValue::as_integer)
85 .map(|v| v as usize)
86 }
87
88 #[must_use]
90 #[allow(clippy::cast_sign_loss)]
91 pub fn get_timeout_ms(&self) -> Option<u64> {
92 self.get("timeout_ms")
93 .and_then(WithValue::as_integer)
94 .map(|v| v as u64)
95 }
96
97 #[must_use]
99 pub fn get_rerank(&self) -> Option<bool> {
100 self.get("rerank").and_then(WithValue::as_bool)
101 }
102}
103
104#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
106pub struct WithOption {
107 pub key: String,
109 pub value: WithValue,
111}
112
113#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
115pub enum WithValue {
116 String(String),
118 Integer(i64),
120 Float(f64),
122 Boolean(bool),
124 Identifier(String),
126}
127
128impl WithValue {
129 #[must_use]
131 pub fn as_str(&self) -> Option<&str> {
132 match self {
133 Self::String(s) | Self::Identifier(s) => Some(s),
134 _ => None,
135 }
136 }
137
138 #[must_use]
140 pub fn as_integer(&self) -> Option<i64> {
141 match self {
142 Self::Integer(i) => Some(*i),
143 _ => None,
144 }
145 }
146
147 #[must_use]
149 pub fn as_float(&self) -> Option<f64> {
150 match self {
151 Self::Float(f) => Some(*f),
152 #[allow(clippy::cast_precision_loss)]
153 Self::Integer(i) => Some(*i as f64),
154 _ => None,
155 }
156 }
157
158 #[must_use]
160 pub fn as_bool(&self) -> Option<bool> {
161 match self {
162 Self::Boolean(b) => Some(*b),
163 _ => None,
164 }
165 }
166}
167
168#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
170pub enum SelectColumns {
171 All,
173 Columns(Vec<Column>),
175}
176
177#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
179pub struct Column {
180 pub name: String,
182 pub alias: Option<String>,
184}
185
186impl Column {
187 #[must_use]
189 pub fn new(name: impl Into<String>) -> Self {
190 Self {
191 name: name.into(),
192 alias: None,
193 }
194 }
195
196 #[must_use]
198 pub fn with_alias(name: impl Into<String>, alias: impl Into<String>) -> Self {
199 Self {
200 name: name.into(),
201 alias: Some(alias.into()),
202 }
203 }
204}
205
206#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
208pub enum Condition {
209 VectorSearch(VectorSearch),
211 VectorFusedSearch(VectorFusedSearch),
213 Comparison(Comparison),
215 In(InCondition),
217 Between(BetweenCondition),
219 Like(LikeCondition),
221 IsNull(IsNullCondition),
223 Match(MatchCondition),
225 And(Box<Condition>, Box<Condition>),
227 Or(Box<Condition>, Box<Condition>),
229 Not(Box<Condition>),
231 Group(Box<Condition>),
233}
234
235#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
240pub struct VectorSearch {
241 pub vector: VectorExpr,
243}
244
245#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
256pub struct VectorFusedSearch {
257 pub vectors: Vec<VectorExpr>,
259 pub fusion: FusionConfig,
261}
262
263#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
265pub struct FusionConfig {
266 pub strategy: String,
268 pub params: std::collections::HashMap<String, f64>,
270}
271
272impl Default for FusionConfig {
273 fn default() -> Self {
274 Self {
275 strategy: "rrf".to_string(),
276 params: std::collections::HashMap::new(),
277 }
278 }
279}
280
281impl FusionConfig {
282 #[must_use]
284 pub fn rrf() -> Self {
285 let mut params = std::collections::HashMap::new();
286 params.insert("k".to_string(), 60.0);
287 Self {
288 strategy: "rrf".to_string(),
289 params,
290 }
291 }
292
293 #[must_use]
295 pub fn weighted(avg_weight: f64, max_weight: f64, hit_weight: f64) -> Self {
296 let mut params = std::collections::HashMap::new();
297 params.insert("avg_weight".to_string(), avg_weight);
298 params.insert("max_weight".to_string(), max_weight);
299 params.insert("hit_weight".to_string(), hit_weight);
300 Self {
301 strategy: "weighted".to_string(),
302 params,
303 }
304 }
305}
306
307#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
309pub enum VectorExpr {
310 Literal(Vec<f32>),
312 Parameter(String),
314}
315
316#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
318pub struct Comparison {
319 pub column: String,
321 pub operator: CompareOp,
323 pub value: Value,
325}
326
327#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
329pub enum CompareOp {
330 Eq,
332 NotEq,
334 Gt,
336 Gte,
338 Lt,
340 Lte,
342}
343
344#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
346pub struct InCondition {
347 pub column: String,
349 pub values: Vec<Value>,
351}
352
353#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
355pub struct BetweenCondition {
356 pub column: String,
358 pub low: Value,
360 pub high: Value,
362}
363
364#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
366pub struct LikeCondition {
367 pub column: String,
369 pub pattern: String,
371 #[serde(default)]
373 pub case_insensitive: bool,
374}
375
376#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
378pub struct IsNullCondition {
379 pub column: String,
381 pub is_null: bool,
383}
384
385#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
387pub struct MatchCondition {
388 pub column: String,
390 pub query: String,
392}
393
394#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
396pub enum Value {
397 Integer(i64),
399 Float(f64),
401 String(String),
403 Boolean(bool),
405 Null,
407 Parameter(String),
409}
410
411impl From<i64> for Value {
412 fn from(v: i64) -> Self {
413 Self::Integer(v)
414 }
415}
416
417impl From<f64> for Value {
418 fn from(v: f64) -> Self {
419 Self::Float(v)
420 }
421}
422
423impl From<&str> for Value {
424 fn from(v: &str) -> Self {
425 Self::String(v.to_string())
426 }
427}
428
429impl From<String> for Value {
430 fn from(v: String) -> Self {
431 Self::String(v)
432 }
433}
434
435impl From<bool> for Value {
436 fn from(v: bool) -> Self {
437 Self::Boolean(v)
438 }
439}