libsql_orm/
filters.rs

1//! Filtering and search functionality for libsql-orm
2//!
3//! This module provides comprehensive filtering capabilities including simple comparisons,
4//! complex logical combinations, text search, and sorting. It supports building complex
5//! WHERE clauses programmatically with type safety.
6//!
7//! # Basic Filtering
8//!
9//! ```rust
10//! use libsql_orm::{Filter, FilterOperator, Value};
11//!
12//! // Simple equality filter
13//! let filter = Filter::eq("status", "active");
14//!
15//! // Comparison filters
16//! let age_filter = Filter::gt("age", 18i64);
17//! let name_filter = Filter::like("name", "%john%");
18//!
19//! // Convert to FilterOperator for use in queries
20//! let filter_op = FilterOperator::Single(age_filter);
21//! ```
22//!
23//! # Complex Filtering
24//!
25//! ```rust
26//! use libsql_orm::{Filter, FilterOperator};
27//!
28//! // AND combination
29//! let complex_filter = FilterOperator::And(vec![
30//!     FilterOperator::Single(Filter::eq("is_active", true)),
31//!     FilterOperator::Single(Filter::gt("age", 18i64)),
32//!     FilterOperator::Single(Filter::like("email", "%@company.com")),
33//! ]);
34//!
35//! // OR combination
36//! let or_filter = FilterOperator::Or(vec![
37//!     FilterOperator::Single(Filter::eq("role", "admin")),
38//!     FilterOperator::Single(Filter::eq("role", "moderator")),
39//! ]);
40//!
41//! // Nested combinations
42//! let nested = FilterOperator::And(vec![
43//!     complex_filter,
44//!     FilterOperator::Not(Box::new(or_filter)),
45//! ]);
46//! ```
47//!
48//! # Text Search
49//!
50//! ```rust
51//! use libsql_orm::SearchFilter;
52//!
53//! // Search across multiple fields
54//! let search = SearchFilter::new("john", vec!["name", "email"])
55//!     .case_sensitive(false)
56//!     .exact_match(false);
57//!
58//! let filter_op = search.to_filter_operator();
59//! ```
60//!
61//! # Sorting
62//!
63//! ```rust
64//! use libsql_orm::{Sort, SortOrder};
65//!
66//! let sorts = vec![
67//!     Sort::new("created_at", SortOrder::Desc),
68//!     Sort::new("name", SortOrder::Asc),
69//! ];
70//! ```
71
72use crate::{Operator, Value};
73use serde::{Deserialize, Serialize};
74
75/// Filter operator for building complex queries
76///
77/// Represents a complete filter expression that can be a single condition,
78/// a logical combination of multiple conditions, or a custom SQL condition.
79///
80/// # Examples
81///
82/// ```rust
83/// use libsql_orm::{FilterOperator, Filter};
84///
85/// // Single condition
86/// let single = FilterOperator::Single(Filter::eq("status", "active"));
87///
88/// // Multiple conditions with AND
89/// let and_filter = FilterOperator::And(vec![
90///     FilterOperator::Single(Filter::eq("is_active", true)),
91///     FilterOperator::Single(Filter::gt("age", 18i64)),
92/// ]);
93///
94/// // Multiple conditions with OR
95/// let or_filter = FilterOperator::Or(vec![
96///     FilterOperator::Single(Filter::eq("role", "admin")),
97///     FilterOperator::Single(Filter::eq("role", "user")),
98/// ]);
99///
100/// // Negation
101/// let not_filter = FilterOperator::Not(Box::new(single));
102/// ```
103#[derive(Debug, Clone, Serialize, Deserialize)]
104pub enum FilterOperator {
105    /// Single condition
106    Single(Filter),
107    /// AND combination of filters
108    And(Vec<FilterOperator>),
109    /// OR combination of filters
110    Or(Vec<FilterOperator>),
111    /// NOT filter
112    Not(Box<FilterOperator>),
113    /// Custom SQL condition
114    Custom(String),
115}
116
117/// Individual filter condition
118///
119/// Represents a single comparison operation between a column and a value.
120/// Provides convenient constructor methods for common comparison operations.
121///
122/// # Examples
123///
124/// ```rust
125/// use libsql_orm::{Filter, Value};
126///
127/// // Basic comparisons
128/// let eq_filter = Filter::eq("status", "active");
129/// let gt_filter = Filter::gt("age", 18i64);
130/// let like_filter = Filter::like("name", "%john%");
131///
132/// // Range and list operations
133/// let between_filter = Filter::between("score", 80, 100);
134/// let in_filter = Filter::in_values("role", vec!["admin", "user"]);
135/// let null_filter = Filter::is_null("deleted_at");
136/// ```
137#[derive(Debug, Clone, Serialize, Deserialize)]
138pub struct Filter {
139    /// Column name
140    pub column: String,
141    /// Operator
142    pub operator: Operator,
143    /// Value(s) to compare against
144    pub value: FilterValue,
145}
146
147/// Filter value that can be a single value or multiple values
148///
149/// Supports different value types for various SQL operations:
150/// - Single values for basic comparisons (=, >, <, etc.)
151/// - Multiple values for IN/NOT IN operations  
152/// - Range values for BETWEEN/NOT BETWEEN operations
153#[derive(Debug, Clone, Serialize, Deserialize)]
154pub enum FilterValue {
155    /// Single value
156    Single(Value),
157    /// Multiple values (for IN, NOT IN operators)
158    Multiple(Vec<Value>),
159    /// Range values (for BETWEEN, NOT BETWEEN operators)
160    Range(Value, Value),
161}
162
163impl Filter {
164    /// Create a new filter
165    pub fn new(column: impl Into<String>, operator: Operator, value: FilterValue) -> Self {
166        Self {
167            column: column.into(),
168            operator,
169            value,
170        }
171    }
172
173    /// Create a new filter with a simple value
174    pub fn new_simple(
175        column: impl Into<String>,
176        operator: Operator,
177        value: impl Into<Value>,
178    ) -> Self {
179        Self {
180            column: column.into(),
181            operator,
182            value: FilterValue::Single(value.into()),
183        }
184    }
185
186    /// Create an equality filter
187    pub fn eq(column: impl Into<String>, value: impl Into<Value>) -> Self {
188        Self::new(column, Operator::Eq, FilterValue::Single(value.into()))
189    }
190
191    /// Create a not-equal filter
192    pub fn ne(column: impl Into<String>, value: impl Into<Value>) -> Self {
193        Self::new(column, Operator::Ne, FilterValue::Single(value.into()))
194    }
195
196    /// Create a less-than filter
197    pub fn lt(column: impl Into<String>, value: impl Into<Value>) -> Self {
198        Self::new(column, Operator::Lt, FilterValue::Single(value.into()))
199    }
200
201    /// Create a less-than-or-equal filter
202    pub fn le(column: impl Into<String>, value: impl Into<Value>) -> Self {
203        Self::new(column, Operator::Le, FilterValue::Single(value.into()))
204    }
205
206    /// Create a greater-than filter
207    pub fn gt(column: impl Into<String>, value: impl Into<Value>) -> Self {
208        Self::new(column, Operator::Gt, FilterValue::Single(value.into()))
209    }
210
211    /// Create a greater-than-or-equal filter
212    pub fn ge(column: impl Into<String>, value: impl Into<Value>) -> Self {
213        Self::new(column, Operator::Ge, FilterValue::Single(value.into()))
214    }
215
216    /// Create a LIKE filter
217    pub fn like(column: impl Into<String>, pattern: impl Into<String>) -> Self {
218        Self::new(
219            column,
220            Operator::Like,
221            FilterValue::Single(Value::Text(pattern.into())),
222        )
223    }
224
225    /// Create a NOT LIKE filter
226    pub fn not_like(column: impl Into<String>, pattern: impl Into<String>) -> Self {
227        Self::new(
228            column,
229            Operator::NotLike,
230            FilterValue::Single(Value::Text(pattern.into())),
231        )
232    }
233
234    /// Create an IN filter
235    pub fn in_values(column: impl Into<String>, values: Vec<impl Into<Value>>) -> Self {
236        let values = values.into_iter().map(|v| v.into()).collect();
237        Self::new(column, Operator::In, FilterValue::Multiple(values))
238    }
239
240    /// Create a NOT IN filter
241    pub fn not_in_values(column: impl Into<String>, values: Vec<impl Into<Value>>) -> Self {
242        let values = values.into_iter().map(|v| v.into()).collect();
243        Self::new(column, Operator::NotIn, FilterValue::Multiple(values))
244    }
245
246    /// Create an IS NULL filter
247    pub fn is_null(column: impl Into<String>) -> Self {
248        Self::new(column, Operator::IsNull, FilterValue::Single(Value::Null))
249    }
250
251    /// Create an IS NOT NULL filter
252    pub fn is_not_null(column: impl Into<String>) -> Self {
253        Self::new(
254            column,
255            Operator::IsNotNull,
256            FilterValue::Single(Value::Null),
257        )
258    }
259
260    /// Create a BETWEEN filter
261    pub fn between(
262        column: impl Into<String>,
263        min: impl Into<Value>,
264        max: impl Into<Value>,
265    ) -> Self {
266        Self::new(
267            column,
268            Operator::Between,
269            FilterValue::Range(min.into(), max.into()),
270        )
271    }
272
273    /// Create a NOT BETWEEN filter
274    pub fn not_between(
275        column: impl Into<String>,
276        min: impl Into<Value>,
277        max: impl Into<Value>,
278    ) -> Self {
279        Self::new(
280            column,
281            Operator::NotBetween,
282            FilterValue::Range(min.into(), max.into()),
283        )
284    }
285}
286
287impl FilterOperator {
288    /// Create an AND filter
289    pub fn and(filters: Vec<FilterOperator>) -> Self {
290        FilterOperator::And(filters)
291    }
292
293    /// Create an OR filter
294    pub fn or(filters: Vec<FilterOperator>) -> Self {
295        FilterOperator::Or(filters)
296    }
297
298    /// Create a NOT filter
299    pub fn negate(filter: FilterOperator) -> Self {
300        FilterOperator::Not(Box::new(filter))
301    }
302
303    /// Add a filter to an AND group
304    pub fn and_with(self, other: FilterOperator) -> Self {
305        match self {
306            FilterOperator::And(mut filters) => {
307                filters.push(other);
308                FilterOperator::And(filters)
309            }
310            _ => FilterOperator::And(vec![self, other]),
311        }
312    }
313
314    /// Add a filter to an OR group
315    pub fn or_with(self, other: FilterOperator) -> Self {
316        match self {
317            FilterOperator::Or(mut filters) => {
318                filters.push(other);
319                FilterOperator::Or(filters)
320            }
321            _ => FilterOperator::Or(vec![self, other]),
322        }
323    }
324}
325
326impl std::ops::Not for FilterOperator {
327    type Output = Self;
328
329    fn not(self) -> Self::Output {
330        FilterOperator::Not(Box::new(self))
331    }
332}
333
334/// Search filter for text-based searches
335///
336/// Provides flexible text search capabilities across one or more columns
337/// with options for case sensitivity and exact matching.
338///
339/// # Examples
340///
341/// ```rust
342/// use libsql_orm::SearchFilter;
343///
344/// // Basic search across multiple fields
345/// let search = SearchFilter::new("john", vec!["name", "email"]);
346///
347/// // Case-sensitive exact match
348/// let exact_search = SearchFilter::new("John Doe", vec!["full_name"])
349///     .case_sensitive(true)
350///     .exact_match(true);
351///
352/// // Convert to filter for use in queries
353/// let filter_op = search.to_filter_operator();
354/// ```
355#[derive(Debug, Clone, Serialize, Deserialize)]
356pub struct SearchFilter {
357    /// Search query
358    pub query: String,
359    /// Columns to search in
360    pub columns: Vec<String>,
361    /// Whether to use case-sensitive search
362    pub case_sensitive: bool,
363    /// Whether to use exact match
364    pub exact_match: bool,
365}
366
367impl SearchFilter {
368    /// Create a new search filter
369    pub fn new(query: impl Into<String>, columns: Vec<impl Into<String>>) -> Self {
370        Self {
371            query: query.into(),
372            columns: columns.into_iter().map(|c| c.into()).collect(),
373            case_sensitive: false,
374            exact_match: false,
375        }
376    }
377
378    /// Set case sensitivity
379    pub fn case_sensitive(mut self, case_sensitive: bool) -> Self {
380        self.case_sensitive = case_sensitive;
381        self
382    }
383
384    /// Set exact match
385    pub fn exact_match(mut self, exact_match: bool) -> Self {
386        self.exact_match = exact_match;
387        self
388    }
389
390    /// Convert to FilterOperator
391    pub fn to_filter_operator(&self) -> FilterOperator {
392        let mut filters = Vec::new();
393
394        for column in &self.columns {
395            let filter = if self.exact_match {
396                Filter::eq(column, &*self.query)
397            } else {
398                Filter::like(column, format!("%{}%", self.query))
399            };
400            filters.push(FilterOperator::Single(filter));
401        }
402
403        if filters.len() == 1 {
404            filters.pop().unwrap()
405        } else {
406            FilterOperator::Or(filters)
407        }
408    }
409
410    /// Create a new search filter for a single field
411    pub fn new_single_field(field: impl Into<String>, query: impl Into<String>) -> Self {
412        Self {
413            query: query.into(),
414            columns: vec![field.into()],
415            case_sensitive: false,
416            exact_match: false,
417        }
418    }
419
420    /// Create a new search filter for multiple fields
421    pub fn new_multiple_fields(fields: Vec<impl Into<String>>, query: impl Into<String>) -> Self {
422        Self {
423            query: query.into(),
424            columns: fields.into_iter().map(|f| f.into()).collect(),
425            case_sensitive: false,
426            exact_match: false,
427        }
428    }
429
430    /// Convert to FilterOperator with improved search logic
431    pub fn to_filter_operator_improved(&self) -> FilterOperator {
432        let mut filters = Vec::new();
433
434        for column in &self.columns {
435            let pattern = if self.exact_match {
436                self.query.clone()
437            } else {
438                format!("%{}%", self.query)
439            };
440
441            let filter = if self.case_sensitive {
442                Filter::like(column.clone(), pattern)
443            } else {
444                // For case-insensitive search, we'll use LOWER() function
445                // This will be handled in the query builder
446                Filter::like(column.clone(), pattern)
447            };
448
449            filters.push(FilterOperator::Single(filter));
450        }
451
452        if filters.len() == 1 {
453            filters.pop().unwrap()
454        } else {
455            FilterOperator::Or(filters)
456        }
457    }
458}
459
460/// Sort specification
461///
462/// Defines how query results should be sorted by column and order.
463/// Multiple sort specifications can be combined to create complex sorting.
464///
465/// # Examples
466///
467/// ```rust
468/// use libsql_orm::{Sort, SortOrder};
469///
470/// // Single column sorts
471/// let name_asc = Sort::asc("name");
472/// let date_desc = Sort::desc("created_at");
473///
474/// // Multiple column sorting
475/// let sorts = vec![
476///     Sort::new("priority", SortOrder::Desc),
477///     Sort::new("name", SortOrder::Asc),
478/// ];
479/// ```
480#[derive(Debug, Clone, Serialize, Deserialize)]
481pub struct Sort {
482    /// Column name
483    pub column: String,
484    /// Sort order
485    pub order: crate::SortOrder,
486}
487
488impl Sort {
489    /// Create a new sort specification
490    pub fn new(column: impl Into<String>, order: crate::SortOrder) -> Self {
491        Self {
492            column: column.into(),
493            order,
494        }
495    }
496
497    /// Create a new sort with boolean flag for ascending
498    pub fn new_bool(column: impl Into<String>, ascending: bool) -> Self {
499        Self {
500            column: column.into(),
501            order: if ascending {
502                crate::SortOrder::Asc
503            } else {
504                crate::SortOrder::Desc
505            },
506        }
507    }
508
509    /// Create an ascending sort
510    pub fn asc(column: impl Into<String>) -> Self {
511        Self::new(column, crate::SortOrder::Asc)
512    }
513
514    /// Create a descending sort
515    pub fn desc(column: impl Into<String>) -> Self {
516        Self::new(column, crate::SortOrder::Desc)
517    }
518}