Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add container-like data types examples #147

Merged
merged 1 commit into from
Sep 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 26 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -398,11 +398,36 @@ See [examples](https://github.com/ClickHouse/clickhouse-rs/tree/main/examples).
}
```
</details>
* `JSON` and `Geo` aren't supported for now.
* `Geo` types are supported. `Point` behaves like a tuple `(f64, f64)`, and the rest of the types are just slices of points.
<details>
<summary>Example</summary>

```rust,ignore
type Point = (f64, f64);
type Ring = Vec<Point>;
type Polygon = Vec<Ring>;
type MultiPolygon = Vec<Polygon>;
type LineString = Vec<Point>;
type MultiLineString = Vec<LineString>;

#[derive(Row, Serialize, Deserialize)]
struct MyRow {
point: Point,
ring: Ring,
polygon: Polygon,
multi_polygon: MultiPolygon,
line_string: LineString,
multi_line_string: MultiLineString,
}
```
</details>

* `JSON`, `Variant`, `Dynamic` types are not supported for now.

See also the additional examples:

* [Simpler ClickHouse data types](examples/data_types_derive_simple.rs)
* [Container-like ClickHouse data types](examples/data_types_derive_containers.rs)

## Mocking
The crate provides utils for mocking CH server and testing DDL, `SELECT`, `INSERT` and `WATCH` queries.
Expand Down
1 change: 1 addition & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ If something is missing, or you found a mistake in one of these examples, please
### Data types

- [data_types_derive_simple.rs](data_types_derive_simple.rs) - deriving simpler ClickHouse data types in a struct. Required cargo features: `time`, `uuid`.
- [data_types_derive_containers.rs](data_types_derive_containers.rs) - deriving container-like (Array, Tuple, Map, Nested, Geo) ClickHouse data types in a struct.

### Special cases

Expand Down
132 changes: 132 additions & 0 deletions examples/data_types_derive_containers.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
use rand::distributions::Alphanumeric;
use rand::Rng;

use clickhouse::sql::Identifier;
use clickhouse::{error::Result, Client};

// This example covers derivation of container-like ClickHouse data types.
// See also:
// - https://clickhouse.com/docs/en/sql-reference/data-types
// - data_types_derive_simple.rs

#[tokio::main]
async fn main() -> Result<()> {
let table_name = "chrs_data_types_derive_containers";
let client = Client::default().with_url("http://localhost:8123");

client
.query(
"
CREATE OR REPLACE TABLE ?
(
arr Array(String),
arr2 Array(Array(String)),
map Map(String, UInt32),
tuple Tuple(String, UInt32),
nested Nested(name String, count UInt32),
point Point,
ring Ring,
polygon Polygon,
multi_polygon MultiPolygon,
line_string LineString,
multi_line_string MultiLineString
) ENGINE MergeTree ORDER BY ();
",
)
.bind(Identifier(table_name))
.execute()
.await?;

let mut insert = client.insert(table_name)?;
insert.write(&Row::new()).await?;
insert.end().await?;

let rows = client
.query("SELECT ?fields FROM ?")
.bind(Identifier(table_name))
.fetch_all::<Row>()
.await?;

println!("{rows:#?}");
Ok(())
}

// See https://clickhouse.com/docs/en/sql-reference/data-types/geo
type Point = (f64, f64);
type Ring = Vec<Point>;
type Polygon = Vec<Ring>;
type MultiPolygon = Vec<Polygon>;
type LineString = Vec<Point>;
type MultiLineString = Vec<LineString>;

#[derive(Clone, Debug, PartialEq)]
#[derive(clickhouse::Row, serde::Serialize, serde::Deserialize)]
pub struct Row {
arr: Vec<String>,
arr2: Vec<Vec<String>>,
map: Vec<(String, u32)>,
tuple: (String, u32),
// Nested columns are internally represented as arrays of the same length
// https://clickhouse.com/docs/en/sql-reference/data-types/nested-data-structures/nested
#[serde(rename = "nested.name")]
nested_name: Vec<String>,
#[serde(rename = "nested.count")]
nested_count: Vec<u32>,
// Geo types
point: Point,
ring: Ring,
polygon: Polygon,
multi_polygon: MultiPolygon,
line_string: LineString,
multi_line_string: MultiLineString,
}

impl Row {
pub fn new() -> Self {
let mut rng = rand::thread_rng();
Row {
arr: vec![random_str()],
arr2: vec![vec![random_str()]],
map: vec![(random_str(), 42)],
tuple: (random_str(), 144),
// Nested
// NB: the length of all vectors/slices representing Nested columns must be the same
nested_name: vec![random_str(), random_str()],
nested_count: vec![rng.gen(), rng.gen()],
// Geo
point: random_point(),
ring: random_ring(),
polygon: random_polygon(),
multi_polygon: vec![random_polygon()],
line_string: random_ring(), // on the type level, the same as the Ring
multi_line_string: random_polygon(), // on the type level, the same as the Polygon
}
}
}

impl Default for Row {
fn default() -> Self {
Self::new()
}
}

fn random_str() -> String {
rand::thread_rng()
.sample_iter(&Alphanumeric)
.take(3)
.map(char::from)
.collect()
}

fn random_point() -> Point {
let mut rng = rand::thread_rng();
(rng.gen(), rng.gen())
}

fn random_ring() -> Ring {
vec![random_point(), random_point()]
}

fn random_polygon() -> Polygon {
vec![random_ring(), random_ring()]
}