Skip to content

Commit d67b99e

Browse files
committed
Feedback: Add and compatibility with other DB Managers
1 parent 2e49008 commit d67b99e

File tree

3 files changed

+81
-40
lines changed

3 files changed

+81
-40
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ members = [".", "modql-macros"]
2828
default = ["modql-macros"]
2929
with-sea-query = ["sea-query", "modql-macros/with-sea-query"]
3030
with-rusqlite = ["rusqlite", "modql-macros/with-rusqlite"]
31-
with-postgres = ["sea-query/backend-postgres"]
31+
with-ilike = ["sea-query/backend-postgres"]
3232

3333
[dependencies]
3434
modql-macros = { version="=0.4.0-rc.6", path = "modql-macros", optional=true}

README.md

Lines changed: 31 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -97,31 +97,37 @@ The following tables show the list of possible operators for each type.
9797

9898
### `OpValString` Operators
9999

100-
| Operator | Meaning | Example |
101-
|---------------------|-----------------------------------------------------------------------------------------------------------|----------------------------------------------------------|
102-
| `$eq` | Exact match with one value | `{name: {"$eq": "Jon Doe"}}` same as `{name: "Jon Doe"}` |
103-
| `$in` | Exact match with within a list of values (or) | `{name: {"$in": ["Alice", "Jon Doe"]}}` |
104-
| `$not` | Exclude any exact match | `{name: {"$not": "Jon Doe"}}` |
105-
| `$notIn` | Exclude any exact withing a list | `{name: {"$notIn": ["Jon Doe"]}}` |
106-
| `$contains` | For string, does a contains | `{name: {"$contains": "Doe"}}` |
107-
| `$containsAny` | For string, match if contained in any of items | `{name: {"$containsAny": ["Doe", "Ali"]}}` |
108-
| `$containsAll` | For string, match if all items are in the src | `{name: {"$containsAll": ["Hello", "World"]}}` |
109-
| `$notContains` | Does not contain | `{name: {"$notContains": "Doe"}}` |
110-
| `$notContainsAny` | Does not call any of (none is contained) | `{name: {"$notContainsAny": ["Doe", "Ali"]}}` |
111-
| `$startsWith` | For string, does a startsWith | `{name: {"$startsWith": "Jon"}}` |
112-
| `$startsWithAny` | For string, match if startsWith in any of items | `{name: {"$startsWithAny": ["Jon", "Al"]}}` |
113-
| `$notStartsWith` | Does not start with | `{name: {"$notStartsWith": "Jon"}}` |
114-
| `$notStartsWithAny` | Does not start with any of the items | `{name: {"$notStartsWithAny": ["Jon", "Al"]}}` |
115-
| `$endsWith` | For string, does and end with | `{name: {"$endsWithAny": "Doe"}}` |
116-
| `$endsWithAny` | For string, does a contains (or) | `{name: {"$endsWithAny": ["Doe", "ice"]}}` |
117-
| `$notEndsWith` | Does not end with | `{name: {"$notEndsWithAny": "Doe"}}` |
118-
| `$notEndsWithAny` | Does not end with any of the items | `{name: {"$notEndsWithAny": ["Doe", "ice"]}}` |
119-
| `$lt` | Lesser Than | `{name: {"$lt": "C"}}` |
120-
| `$lte` | Lesser Than or = | `{name: {"$lte": "C"}}` |
121-
| `$gt` | Greater Than | `{name: {"$gt": "J"}}` |
122-
| `$gte` | Greater Than or = | `{name: {"$gte": "J"}}` |
123-
| `$null` | If the value is null | `{name: {"$null": true}}` |
124-
| `$ilike` | For string, does a contains in a case-insensitive way. Needs `with-postgres` flag enabled in `Cargo.toml` | `{name: {"$ilike": "DoE"}}` |
100+
| Operator | Meaning | Example |
101+
|---------------------|--------------------------------------------------------------------------------------------------------|----------------------------------------------------------|
102+
| `$eq` | Exact match with one value | `{name: {"$eq": "Jon Doe"}}` same as `{name: "Jon Doe"}` |
103+
| `$in` | Exact match with within a list of values (or) | `{name: {"$in": ["Alice", "Jon Doe"]}}` |
104+
| `$not` | Exclude any exact match | `{name: {"$not": "Jon Doe"}}` |
105+
| `$notIn` | Exclude any exact withing a list | `{name: {"$notIn": ["Jon Doe"]}}` |
106+
| `$contains` | For string, does a contains | `{name: {"$contains": "Doe"}}` |
107+
| `$containsAny` | For string, match if contained in any of items | `{name: {"$containsAny": ["Doe", "Ali"]}}` |
108+
| `$containsAll` | For string, match if all items are in the src | `{name: {"$containsAll": ["Hello", "World"]}}` |
109+
| `$notContains` | Does not contain | `{name: {"$notContains": "Doe"}}` |
110+
| `$notContainsAny` | Does not call any of (none is contained) | `{name: {"$notContainsAny": ["Doe", "Ali"]}}` |
111+
| `$startsWith` | For string, does a startsWith | `{name: {"$startsWith": "Jon"}}` |
112+
| `$startsWithAny` | For string, match if startsWith in any of items | `{name: {"$startsWithAny": ["Jon", "Al"]}}` |
113+
| `$notStartsWith` | Does not start with | `{name: {"$notStartsWith": "Jon"}}` |
114+
| `$notStartsWithAny` | Does not start with any of the items | `{name: {"$notStartsWithAny": ["Jon", "Al"]}}` |
115+
| `$endsWith` | For string, does and end with | `{name: {"$endsWithAny": "Doe"}}` |
116+
| `$endsWithAny` | For string, does a contains (or) | `{name: {"$endsWithAny": ["Doe", "ice"]}}` |
117+
| `$notEndsWith` | Does not end with | `{name: {"$notEndsWithAny": "Doe"}}` |
118+
| `$notEndsWithAny` | Does not end with any of the items | `{name: {"$notEndsWithAny": ["Doe", "ice"]}}` |
119+
| `$lt` | Lesser Than | `{name: {"$lt": "C"}}` |
120+
| `$lte` | Lesser Than or = | `{name: {"$lte": "C"}}` |
121+
| `$gt` | Greater Than | `{name: {"$gt": "J"}}` |
122+
| `$gte` | Greater Than or = | `{name: {"$gte": "J"}}` |
123+
| `$null` | If the value is null | `{name: {"$null": true}}` |
124+
| `$containsCi` | For string, does a contains in a case-insensitive way | `{name: {"$containsCi": "doe"}}` |
125+
| `$notContainsCi` | Does not contain in a case-insensitive way | `{name: {"$notContainsCi": "doe"}}` |
126+
| `$startsWithCi` | For string, does a startsWith in a case-insensitive way | `{name: {"$startsWithCi": "jon"}}` |
127+
| `$notStartsWithCi` | Does not start with in a case-insensitive way | `{name: {"$notStartsWithCi": "jon"}}` |
128+
| `$endsWithCi` | For string, does an endsWith in a case-insensitive way | `{name: {"$endsWithCi": "doe"}}` |
129+
| `$notEndsWithCi` | Does not end with in a case-insensitive way | `{name: {"$notEndsWithCi": "doe"}}` |
130+
| `$ilike` | For string, does a contains in a case-insensitive way. Needs `with-ilike` flag enabled in `Cargo.toml` | `{name: {"$ilike": "DoE"}}` |
125131

126132
### `OpValInt32, OpValInt64, OpValFloat64` Operators
127133

src/filter/ops/op_val_string.rs

Lines changed: 49 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,15 @@ pub enum OpValString {
4242
Empty(bool),
4343
Null(bool),
4444

45+
ContainsCi(String),
46+
NotContainsCi(String),
47+
48+
StartsWithCi(String),
49+
NotStartsWithCi(String),
50+
51+
EndsWithCi(String),
52+
NotEndsWithCi(String),
53+
4554
Ilike(String),
4655
}
4756

@@ -163,6 +172,16 @@ mod json {
163172
("$empty", Value::Bool(v)) => OpValString::Empty(v),
164173
("$null", Value::Bool(v)) => OpValString::Null(v),
165174

175+
("$containsCi", Value::String(string_v)) => OpValString::ContainsCi(string_v),
176+
("$notContainsCi", Value::String(string_v)) => OpValString::NotContainsCi(string_v),
177+
178+
("$startsWithCi", Value::String(string_v)) => OpValString::StartsWithCi(string_v),
179+
("$notStartsWithCi", Value::String(string_v)) => OpValString::NotStartsWithCi(string_v),
180+
181+
("$endsWithCi", Value::String(string_v)) => OpValString::EndsWithCi(string_v),
182+
("$notEndsWithCi", Value::String(string_v)) => OpValString::NotEndsWithCi(string_v),
183+
184+
// Postgres optimized case insensitive like
166185
("$ilike", Value::String(string_v)) => OpValString::Ilike(string_v),
167186

168187
(_, v) => {
@@ -183,9 +202,9 @@ mod with_sea_query {
183202
use super::*;
184203
use crate::filter::{sea_is_col_value_null, FilterNodeOptions, SeaResult};
185204
use crate::into_node_value_expr;
186-
use sea_query::{BinOper, ColumnRef, Condition, ConditionExpression, SimpleExpr};
205+
use sea_query::{BinOper, ColumnRef, Condition, ConditionExpression, Expr, Func, SimpleExpr};
187206

188-
#[cfg(feature = "with-postgres")]
207+
#[cfg(feature = "with-ilike")]
189208
use sea_query::extension::postgres::PgBinOper;
190209

191210
impl OpValString {
@@ -199,15 +218,15 @@ mod with_sea_query {
199218
ConditionExpression::SimpleExpr(SimpleExpr::binary(col.clone().into(), op, vxpr))
200219
};
201220

202-
#[cfg(feature = "with-postgres")]
203-
let pg_binary_fn = |op: PgBinOper, v: String| {
204-
let vxpr = into_node_value_expr(v, node_options);
205-
ConditionExpression::SimpleExpr(SimpleExpr::binary(
206-
col.clone().into(),
207-
BinOper::PgOperator(op),
208-
vxpr,
209-
))
210-
};
221+
#[cfg(feature = "with-ilike")]
222+
let pg_binary_fn = |op: PgBinOper, v: String| {
223+
let vxpr = into_node_value_expr(v, node_options);
224+
ConditionExpression::SimpleExpr(SimpleExpr::binary(
225+
col.clone().into(),
226+
BinOper::PgOperator(op),
227+
vxpr,
228+
))
229+
};
211230

212231
let binaries_fn = |op: BinOper, v: Vec<String>| {
213232
let vxpr_list: Vec<SimpleExpr> = v.into_iter().map(|v| into_node_value_expr(v, node_options)).collect();
@@ -226,6 +245,13 @@ mod with_sea_query {
226245
ConditionExpression::Condition(cond)
227246
};
228247

248+
let case_insensitive_fn = |op: BinOper, v: String| {
249+
let vxpr = SimpleExpr::Value(v.into());
250+
let col_expr = SimpleExpr::FunctionCall(Func::lower(Expr::col(col.clone())).into());
251+
let value_expr = SimpleExpr::FunctionCall(Func::lower(vxpr).into());
252+
ConditionExpression::SimpleExpr(SimpleExpr::binary(col_expr, op, value_expr))
253+
};
254+
229255
let cond = match self {
230256
OpValString::Eq(s) => binary_fn(BinOper::Equal, s),
231257
OpValString::Not(s) => binary_fn(BinOper::NotEqual, s),
@@ -275,14 +301,23 @@ mod with_sea_query {
275301
.into()
276302
}
277303

304+
OpValString::ContainsCi(s) => case_insensitive_fn(BinOper::Like, format!("%{s}%")),
305+
OpValString::NotContainsCi(s) => case_insensitive_fn(BinOper::NotLike, format!("%{s}%")),
306+
307+
OpValString::StartsWithCi(s) => case_insensitive_fn(BinOper::Like, format!("{s}%")),
308+
OpValString::NotStartsWithCi(s) => case_insensitive_fn(BinOper::NotLike, format!("{s}%")),
309+
310+
OpValString::EndsWithCi(s) => case_insensitive_fn(BinOper::Like, format!("%{s}")),
311+
OpValString::NotEndsWithCi(s) => case_insensitive_fn(BinOper::NotLike, format!("%{s}")),
312+
278313
OpValString::Ilike(s) => {
279-
#[cfg(feature = "with-postgres")]
314+
#[cfg(feature = "with-ilike")]
280315
{
281316
pg_binary_fn(PgBinOper::ILike, format!("%{s}%"))
282317
}
283-
#[cfg(not(feature = "with-postgres"))]
318+
#[cfg(not(feature = "with-ilike"))]
284319
{
285-
binary_fn(BinOper::Like, format!("%{s}%"))
320+
case_insensitive_fn(BinOper::Like, format!("%{s}%"))
286321
}
287322
},
288323
};

0 commit comments

Comments
 (0)