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}