From f58b9213ee81ab7c9ad6f71c2719f20af393798b Mon Sep 17 00:00:00 2001 From: slvrtrn Date: Tue, 10 Sep 2024 16:05:29 +0200 Subject: [PATCH] Add container-like data types examples --- README.md | 27 ++++- examples/README.md | 1 + examples/data_types_derive_containers.rs | 132 +++++++++++++++++++++++ 3 files changed, 159 insertions(+), 1 deletion(-) create mode 100644 examples/data_types_derive_containers.rs diff --git a/README.md b/README.md index c6cd1bb..4a3753d 100644 --- a/README.md +++ b/README.md @@ -398,11 +398,36 @@ See [examples](https://github.com/ClickHouse/clickhouse-rs/tree/main/examples). } ``` -* `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. +
+ Example + + ```rust,ignore + type Point = (f64, f64); + type Ring = Vec; + type Polygon = Vec; + type MultiPolygon = Vec; + type LineString = Vec; + type MultiLineString = Vec; + + #[derive(Row, Serialize, Deserialize)] + struct MyRow { + point: Point, + ring: Ring, + polygon: Polygon, + multi_polygon: MultiPolygon, + line_string: LineString, + multi_line_string: MultiLineString, + } + ``` +
+ +* `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. diff --git a/examples/README.md b/examples/README.md index 538389d..35e4270 100644 --- a/examples/README.md +++ b/examples/README.md @@ -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 diff --git a/examples/data_types_derive_containers.rs b/examples/data_types_derive_containers.rs new file mode 100644 index 0000000..f8dee27 --- /dev/null +++ b/examples/data_types_derive_containers.rs @@ -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::() + .await?; + + println!("{rows:#?}"); + Ok(()) +} + +// See https://clickhouse.com/docs/en/sql-reference/data-types/geo +type Point = (f64, f64); +type Ring = Vec; +type Polygon = Vec; +type MultiPolygon = Vec; +type LineString = Vec; +type MultiLineString = Vec; + +#[derive(Clone, Debug, PartialEq)] +#[derive(clickhouse::Row, serde::Serialize, serde::Deserialize)] +pub struct Row { + arr: Vec, + arr2: Vec>, + 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, + #[serde(rename = "nested.count")] + nested_count: Vec, + // 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()] +}