Skip to content

Commit dcd2459

Browse files
author
system
committed
Extend Filters + Docs
1 parent 3aa8e7e commit dcd2459

2 files changed

Lines changed: 182 additions & 21 deletions

File tree

rwf/src/model/filter.rs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ pub struct WhereClause {
1111
}
1212

1313
#[derive(Debug, Clone, crate::prelude::Deserialize, crate::prelude::Serialize)]
14-
enum Comparison {
14+
pub(super) enum Comparison {
1515
/// x = 1
1616
Equal((Column, Value)),
1717
/// x IN (1, 2, 3)
@@ -32,6 +32,8 @@ enum Comparison {
3232
StartsWith((Column, Value)),
3333
/// x LIKE '%hello'
3434
EndsWith((Column, Value)),
35+
/// Negates the inner operation.
36+
/// x = y => x <> y
3537
Negation(Box<Self>),
3638
}
3739
impl Not for Comparison {
@@ -50,6 +52,23 @@ impl Not for Comparison {
5052
}
5153

5254
impl Comparison {
55+
pub(super) fn new(op: super::select::Op, column: Column, value: Value) -> Self {
56+
use super::select::Op;
57+
match op {
58+
Op::Equals => match value {
59+
Value::Record(val) => Self::In((column, *val)),
60+
val => Self::Equal((column, val)),
61+
},
62+
Op::LesserThan => Self::LesserThan((column, value)),
63+
Op::GreaterThan => Self::GreaterThan((column, value)),
64+
Op::GreaterEqualThan => Self::GreaterEqualThan((column, value)),
65+
Op::LesserEqualThan => Self::LesserEqualThan((column, value)),
66+
Op::StartsWith => Self::StartsWith((column, value)),
67+
Op::EndsWith => Self::EndsWith((column, value)),
68+
Op::Contains => Self::Contains((column, value)),
69+
Op::Negation(inner) => !Self::new(*inner, column, value),
70+
}
71+
}
5372
fn placeholder(&self) -> bool {
5473
use Comparison::*;
5574

@@ -264,6 +283,10 @@ impl Filter {
264283
self.clauses.is_empty()
265284
}
266285

286+
pub(super) fn push(&mut self, clause: Comparison) {
287+
self.clauses.push(clause);
288+
}
289+
267290
/// Add a predicate to the filter, using the AND operator.
268291
pub fn add(&mut self, column: Column, value: impl ToValue) {
269292
let value = value.to_value();

rwf/src/model/select.rs

Lines changed: 158 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -295,26 +295,7 @@ pub trait FilterQuery: Sized {
295295
value
296296
};
297297

298-
let mut mapper = |o: Op, col: Column, val: Value| {
299-
match o {
300-
Op::Equals => filter.add(col, val),
301-
Op::LesserThan => filter.lt(col, val),
302-
Op::GreaterThan => filter.gt(col, val),
303-
Op::GreaterEqualThan => filter.gte(col, val),
304-
Op::LesserEqualThan => filter.lte(col, val),
305-
Op::StartsWith => filter.starts_with(col, val),
306-
Op::EndsWith => filter.ends_with(col, val),
307-
Op::Contains => filter.contains(col, val),
308-
operation => unimplemented!("Direct conversion for Op {:?} into Clause is not implenented. Negation should be handelt outside of this clousure.", operation)
309-
}
310-
};
311-
match op {
312-
Op::Negation(inner) => {
313-
mapper(*inner, column, value);
314-
filter.negate_last();
315-
}
316-
operation => mapper(operation, column, value),
317-
}
298+
filter.push(super::filter::Comparison::new(op, column, value));
318299

319300
match join_op {
320301
JoinOp::And => self.get_where_clause_mut().concat(filter),
@@ -674,6 +655,163 @@ pub trait FilterQuery: Sized {
674655
self = self.filter(column, value, JoinOp::And, Op::EndsWith);
675656
self
676657
}
658+
/// Filter String Column for values not ending with a specific substring it ends with
659+
/// # Example
660+
/// ```
661+
/// use rwf::model::prelude::*;
662+
/// use rwf::model::placeholders::Placeholders;
663+
/// #[derive(Debug, Clone, rwf::macros::Model, rwf::prelude::Serialize, rwf::prelude::Deserialize)]
664+
/// struct User {
665+
/// id: Option<i64>,
666+
/// name: String,
667+
/// mail: String
668+
/// }
669+
/// let query = User::all().filter_not_ends_with("mail", ".tld");
670+
/// assert_eq!(
671+
/// query.to_sql(),
672+
/// r#"SELECT * FROM "users" WHERE "users"."mail" NOT LIKE '%' || $1"#
673+
/// )
674+
///
675+
///
676+
/// ```
677+
fn filter_not_ends_with(mut self, column: impl ToColumn, value: impl ToValue) -> Self {
678+
self = self.filter(column, value, JoinOp::And, !Op::EndsWith);
679+
self
680+
}
681+
/// Filter by a String column for a Substring. Combine with an or clause
682+
/// # Example
683+
/// ```
684+
/// use rwf::model::prelude::*;
685+
/// use rwf::model::select::Select;
686+
/// use rwf::model::Placeholders;
687+
/// #[derive(Debug, Clone, rwf::macros::Model, rwf::prelude::Serialize, rwf::prelude::Deserialize)]
688+
/// struct User {
689+
/// id:Option<i64>,
690+
/// name: String,
691+
/// }
692+
/// let select: Select<User> = Select::new("users", "id").filter_gt("id", 0).filter_or_contains("name", "es");
693+
/// assert_eq!(
694+
/// select.to_sql(),
695+
/// r#"SELECT * FROM "users" WHERE ("users"."id" > $1) OR ("users"."name" LIKE '%' || $2 || '%')"#
696+
/// )
697+
///
698+
/// ```
699+
fn filter_or_contains(mut self, column: impl ToColumn, value: impl ToValue) -> Self {
700+
self = self.filter(column, value, JoinOp::Or, Op::Contains);
701+
self
702+
}
703+
/// Filter a Text column for values not containing the Value
704+
/// # Example
705+
/// ```
706+
/// use rwf::model::prelude::*;
707+
/// use rwf::model::select::Select;
708+
/// use rwf::model::Placeholders;
709+
/// #[derive(Debug, Clone, rwf::macros::Model, rwf::prelude::Serialize, rwf::prelude::Deserialize)]
710+
/// struct User {
711+
/// id:Option<i64>,
712+
/// name: String,
713+
/// }
714+
/// let select: Select<User> = Select::new("users", "id").filter_gt("id", 0).filter_or_not_contains("name", "es");
715+
/// assert_eq!(
716+
/// select.to_sql(),
717+
/// r#"SELECT * FROM "users" WHERE ("users"."id" > $1) OR ("users"."name" NOT LIKE '%' || $2 || '%')"#
718+
/// )
719+
///
720+
/// ```
721+
fn filter_or_not_contains(mut self, column: impl ToColumn, value: impl ToValue) -> Self {
722+
self = self.filter(column, value, JoinOp::Or, !Op::Contains);
723+
self
724+
}
725+
/// Filter a String column for entries that starts with a string
726+
/// # Example
727+
/// ```
728+
///use rwf::model::prelude::*;
729+
///use rwf::model::Placeholders;
730+
/// #[derive(Debug, Clone, rwf::macros::Model, rwf::prelude::Serialize, rwf::prelude::Deserialize)]
731+
/// struct User {
732+
/// id: Option<i64>,
733+
/// name: String,
734+
/// mail: String
735+
/// }
736+
/// let query = User::all().filter_gt("id", 0).filter_or_starts_with("mail", "name".to_column());
737+
/// assert_eq!(
738+
/// query.to_sql(),
739+
/// r#"SELECT * FROM "users" WHERE ("users"."id" > $1) OR ("users"."mail" LIKE "name" || '%')"#
740+
/// );
741+
///
742+
/// ```
743+
fn filter_or_starts_with(mut self, column: impl ToColumn, value: impl ToValue) -> Self {
744+
self = self.filter(column, value, JoinOp::Or, Op::StartsWith);
745+
self
746+
}
747+
/// Filter a String column for entries that starts with a string
748+
/// # Example
749+
/// ```
750+
///use rwf::model::Placeholders;
751+
///use rwf::model::prelude::*;
752+
/// #[derive(Debug, Clone, rwf::macros::Model, rwf::prelude::Serialize, rwf::prelude::Deserialize)]
753+
/// struct User {
754+
/// id: Option<i64>,
755+
/// name: String,
756+
/// mail: String
757+
/// }
758+
/// let query = User::all().filter_gt("id", 0).filter_or_not_starts_with("mail", "name".to_column());
759+
/// assert_eq!(
760+
/// query.to_sql(),
761+
/// r#"SELECT * FROM "users" WHERE ("users"."id" > $1) OR ("users"."mail" NOT LIKE "name" || '%')"#
762+
/// );
763+
///
764+
/// ```
765+
fn filter_or_not_starts_with(mut self, column: impl ToColumn, value: impl ToValue) -> Self {
766+
self = self.filter(column, value, JoinOp::Or, !Op::StartsWith);
767+
self
768+
}
769+
/// Filter String Column for a substring it ends with
770+
/// # Example
771+
/// ```
772+
/// use rwf::model::prelude::*;
773+
/// use rwf::model::placeholders::Placeholders;
774+
/// #[derive(Debug, Clone, rwf::macros::Model, rwf::prelude::Serialize, rwf::prelude::Deserialize)]
775+
/// struct User {
776+
/// id: Option<i64>,
777+
/// name: String,
778+
/// mail: String
779+
/// }
780+
/// let query = User::all().filter_gt("id", 0).filter_or_ends_with("mail", ".tld");
781+
/// assert_eq!(
782+
/// query.to_sql(),
783+
/// r#"SELECT * FROM "users" WHERE ("users"."id" > $1) OR ("users"."mail" LIKE '%' || $2)"#
784+
/// )
785+
///
786+
///
787+
/// ```
788+
fn filter_or_ends_with(mut self, column: impl ToColumn, value: impl ToValue) -> Self {
789+
self = self.filter(column, value, JoinOp::Or, Op::EndsWith);
790+
self
791+
}
792+
/// Filter String Column for values not ending with a specific substring it ends with
793+
/// # Example
794+
/// ```
795+
/// use rwf::model::prelude::*;
796+
/// use rwf::model::placeholders::Placeholders;
797+
/// #[derive(Debug, Clone, rwf::macros::Model, rwf::prelude::Serialize, rwf::prelude::Deserialize)]
798+
/// struct User {
799+
/// id: Option<i64>,
800+
/// name: String,
801+
/// mail: String
802+
/// }
803+
/// let query = User::all().filter_gt("id", 0).filter_or_not_ends_with("mail", ".tld");
804+
/// assert_eq!(
805+
/// query.to_sql(),
806+
/// r#"SELECT * FROM "users" WHERE ("users"."id" > $1) OR ("users"."mail" NOT LIKE '%' || $2)"#
807+
/// )
808+
///
809+
///
810+
/// ```
811+
fn filter_or_not_ends_with(mut self, column: impl ToColumn, value: impl ToValue) -> Self {
812+
self = self.filter(column, value, JoinOp::Or, !Op::EndsWith);
813+
self
814+
}
677815
}
678816

679817
impl<T: FromRow> FilterQuery for Select<T> {

0 commit comments

Comments
 (0)