Skip to content

Commit

Permalink
Add QueryBuilder and MutationBuilder
Browse files Browse the repository at this point in the history
  • Loading branch information
photino committed Aug 1, 2023
1 parent 6e01754 commit 17680ac
Show file tree
Hide file tree
Showing 21 changed files with 362 additions and 113 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ cargo run -- --env=dev
|-----------------|------------------------|--------------|---------------|
| [`zino-core`] | Core types and traits. | [![Crates.io](https://img.shields.io/crates/v/zino-core)][zino-core] | [![Documentation](https://shields.io/docsrs/zino-core)][zino-core-docs] |
| [`zino-derive`] | Derived traits. | [![Crates.io](https://img.shields.io/crates/v/zino-derive)][zino-derive] | [![Documentation](https://shields.io/docsrs/zino-derive)][zino-derive-docs] |
| [`zino-model`] | Model types. | [![Crates.io](https://img.shields.io/crates/v/zino-model)][zino-model] | [![Documentation](https://shields.io/docsrs/zino-model)][zino-model-docs] |
| [`zino-model`] | Domain models. | [![Crates.io](https://img.shields.io/crates/v/zino-model)][zino-model] | [![Documentation](https://shields.io/docsrs/zino-model)][zino-model-docs] |

## License

Expand Down
Empty file.
1 change: 1 addition & 0 deletions examples/actix-app/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#![feature(let_chains)]

mod controller;
mod domain;
mod extension;
mod logic;
mod middleware;
Expand Down
Empty file.
1 change: 1 addition & 0 deletions examples/axum-app/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#![feature(let_chains)]

mod controller;
mod domain;
mod extension;
mod logic;
mod middleware;
Expand Down
7 changes: 4 additions & 3 deletions zino-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ connector-http = ["connector"]
connector-mysql = ["connector", "sqlx", "sqlx/mysql"]
connector-postgres = ["connector", "sqlx", "sqlx/postgres"]
connector-sqlite = ["connector", "sqlx", "sqlx/sqlite"]
default = ["runtime-tokio"]
default = ["runtime-tokio", "tls-rustls"]
format = []
format-pdf = ["format", "dep:printpdf"]
full = [
Expand Down Expand Up @@ -143,7 +143,7 @@ optional = true
features = ["debug", "loader"]

[dependencies.opendal]
version = "0.38.1"
version = "0.39.0"
optional = true
features = ["layers-all"]

Expand All @@ -153,6 +153,7 @@ optional = true

[dependencies.reqwest]
version = "0.11.18"
default-features = false
features = [
"cookies",
"gzip",
Expand Down Expand Up @@ -210,7 +211,7 @@ features = [

[dev-dependencies]
anyhow = "1.0.72"
base64 = "0.21.2"
base64-simd = "0.8.0"
criterion = "0.5.1"
ryu = "1.0.15"
tinyvec = { version = "1.6.0", features = ["alloc"] }
Expand Down
2 changes: 2 additions & 0 deletions zino-core/benches/criterion_main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ mod format_duration;
mod hashmap_vec;
mod json_raw_value;
mod serde_map;
mod str_join;
mod uuid_simd;

criterion::criterion_group!(
Expand All @@ -14,6 +15,7 @@ criterion::criterion_group!(
hashmap_vec::bench,
json_raw_value::bench,
serde_map::bench,
str_join::bench,
uuid_simd::bench,
);
criterion::criterion_main!(benches);
23 changes: 23 additions & 0 deletions zino-core/benches/str_join.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
pub fn bench(c: &mut criterion::Criterion) {
c.bench_function("str_format", |b| {
b.iter(|| {
let model_name = "user";
let field = "tags";
format!("{model_name}.{field}")
})
});
c.bench_function("str_join", |b| {
b.iter(|| {
let model_name = "user";
let field = "tags";
[model_name, field].join(".")
})
});
c.bench_function("str_add", |b| {
b.iter(|| {
let model_name = "user";
let field = "tags";
String::from(model_name) + "." + field
})
});
}
2 changes: 1 addition & 1 deletion zino-core/src/database/accessor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,7 @@ where
let mut query = Self::default_query();
query.deny_fields(&["content", "extra"]);
query.add_filter("status", Map::from_entry("$ne", "Deleted"));
query.set_sort_order("updated_at", false);
query.set_sort_order("updated_at", true);
query
}

Expand Down
24 changes: 16 additions & 8 deletions zino-core/src/database/mysql.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,17 +168,17 @@ impl<'c> EncodeColumn<DatabaseDriver> for Column<'c> {
for (name, value) in filter {
let operator = match name.as_str() {
"$eq" => "=",
"$ne" | "$neq" => "<>",
"$ne" => "<>",
"$lt" => "<",
"$le" | "$lte" => "<=",
"$le" => "<=",
"$gt" => ">",
"$ge" | "$gte" => ">=",
"$ge" => ">=",
"$in" => "IN",
"$nin" => "NOT IN",
"$like" => "LIKE",
"$ilike" => "ILIKE",
"$rlike" | "$regexp" => "RLIKE",
"$rlike" => "RLIKE",
"$is" => "IS",
"$in" => "IN",
"$nin" => "NOT IN",
_ => "=",
};
if operator == "IN" || operator == "NOT IN" {
Expand Down Expand Up @@ -265,15 +265,23 @@ impl<'c> EncodeColumn<DatabaseDriver> for Column<'c> {
format!(r#"({field} = '') IS NOT FALSE"#)
} else if value == "notnull" {
format!(r#"({field} = '') IS FALSE"#)
} else if value.contains(',') {
let value = value
.split(',')
.map(Query::escape_string)
.collect::<Vec<_>>()
.join(",");
format!(r#"{field} IN ({value})"#)
} else {
let index = value.find(|ch| !"!~*".contains(ch)).unwrap_or(0);
if index > 0 {
let (operator, value) = value.split_at(index);
let value = Query::escape_string(value);
format!(r#"{field} {operator} {value}"#)
} else {
let operator = self.default_value().map(|_| "=").unwrap_or("RLIKE");
let value = Query::escape_string(value);
format!(r#"{field} = {value}"#)
format!(r#"{field} {operator} {value}"#)
}
}
} else {
Expand Down Expand Up @@ -506,7 +514,7 @@ impl QueryExt<DatabaseDriver> for Query {
}

#[inline]
fn query_order(&self) -> (&str, bool) {
fn query_order(&self) -> &[(SharedString, bool)] {
self.sort_order()
}

Expand Down
24 changes: 16 additions & 8 deletions zino-core/src/database/postgres.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,17 +179,17 @@ impl<'c> EncodeColumn<DatabaseDriver> for Column<'c> {
for (name, value) in filter {
let operator = match name.as_str() {
"$eq" => "=",
"$ne" | "$neq" => "<>",
"$ne" => "<>",
"$lt" => "<",
"$le" | "$lte" => "<=",
"$le" => "<=",
"$gt" => ">",
"$ge" | "$gte" => ">=",
"$ge" => ">=",
"$in" => "IN",
"$nin" => "NOT IN",
"$like" => "LIKE",
"$ilike" => "ILIKE",
"$rlike" | "$regexp" => "~*",
"$rlike" => "~*",
"$is" => "IS",
"$in" => "IN",
"$nin" => "NOT IN",
"$all" => "@>",
"$size" => "array_length",
_ => "=",
Expand Down Expand Up @@ -283,15 +283,23 @@ impl<'c> EncodeColumn<DatabaseDriver> for Column<'c> {
format!(r#"({field} = '') IS NOT FALSE"#)
} else if value == "notnull" {
format!(r#"({field} = '') IS FALSE"#)
} else if value.contains(',') {
let value = value
.split(',')
.map(Query::escape_string)
.collect::<Vec<_>>()
.join(",");
format!(r#"{field} IN ({value})"#)
} else {
let index = value.find(|ch| !"!~*".contains(ch)).unwrap_or(0);
if index > 0 {
let (operator, value) = value.split_at(index);
let value = Query::escape_string(value);
format!(r#"{field} {operator} {value}"#)
} else {
let operator = self.default_value().map(|_| "=").unwrap_or("~*");
let value = Query::escape_string(value);
format!(r#"{field} = {value}"#)
format!(r#"{field} {operator} {value}"#)
}
}
} else {
Expand Down Expand Up @@ -522,7 +530,7 @@ impl QueryExt<DatabaseDriver> for Query {
}

#[inline]
fn query_order(&self) -> (&str, bool) {
fn query_order(&self) -> &[(SharedString, bool)] {
self.sort_order()
}

Expand Down
43 changes: 19 additions & 24 deletions zino-core/src/database/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ pub(super) trait QueryExt<DB> {
fn query_filters(&self) -> &Map;

/// Returns the sort order.
fn query_order(&self) -> (&str, bool);
fn query_order(&self) -> &[(SharedString, bool)];

/// Returns the query offset.
fn query_offset(&self) -> usize;
Expand Down Expand Up @@ -73,7 +73,6 @@ pub(super) trait QueryExt<DB> {
return String::new();
}

let (sort_by, ascending) = self.query_order();
let mut expression = String::new();
let mut conditions = Vec::with_capacity(filters.len());
for (key, value) in filters {
Expand Down Expand Up @@ -117,15 +116,7 @@ pub(super) trait QueryExt<DB> {
}
_ => {
if let Some(col) = M::get_column(key) {
let condition = if key == sort_by && col.type_name() == "DateTime" {
// Use the filter condition to optimize pagination offset.
let key = Self::format_field(key);
let operator = if ascending { ">" } else { "<" };
let value = col.encode_value(Some(value));
format!(r#"{key} {operator} {value}"#)
} else {
col.format_filter(key, value)
};
let condition = col.format_filter(key, value);
if !condition.is_empty() {
conditions.push(condition);
}
Expand Down Expand Up @@ -154,12 +145,21 @@ pub(super) trait QueryExt<DB> {

/// Formats the query sort to generate SQL `ORDER BY` expression.
fn format_sort(&self) -> String {
let (sort_by, ascending) = self.query_order();
if sort_by.is_empty() {
let sort_order = self.query_order();
if sort_order.is_empty() {
String::new()
} else {
let sort_order = if ascending { "ASC" } else { "DESC" };
format!("ORDER BY {sort_by} {sort_order}")
let sort_order = sort_order
.iter()
.map(|(sort, descending)| {
if *descending {
format!("{sort} DESC")
} else {
format!("{sort} ASC")
}
})
.collect::<Vec<_>>();
format!("ORDER BY {}", sort_order.join(", "))
}
}

Expand All @@ -170,13 +170,8 @@ pub(super) trait QueryExt<DB> {
return String::new();
}

let (sort_by, _) = self.query_order();
if self.query_filters().contains_key(sort_by) {
format!("LIMIT {limit}")
} else {
let offset = self.query_offset();
format!("LIMIT {limit} OFFSET {offset}")
}
let offset = self.query_offset();
format!("LIMIT {limit} OFFSET {offset}")
}

// Formats the selection with a logic operator.
Expand Down Expand Up @@ -247,9 +242,9 @@ pub(super) trait QueryExt<DB> {
"$eq" => "=",
"$ne" => "<>",
"$lt" => "<",
"$lte" => "<=",
"$le" => "<=",
"$gt" => ">",
"$gte" => ">=",
"$ge" => ">=",
_ => "=",
};
let field = Self::format_field(key);
Expand Down
Loading

0 comments on commit 17680ac

Please sign in to comment.