diff --git a/README.md b/README.md index 66411c4..ccec331 100644 --- a/README.md +++ b/README.md @@ -438,8 +438,8 @@ How to choose between all these features? Here are some considerations: } ``` - -* `JSON`, `Variant`, `Dynamic` types are not supported for now. +* [New `JSON` data type](https://clickhouse.com/docs/en/sql-reference/data-types/newjson) is currently supported as a string when using ClickHouse 24.10+. See [this example](examples/data_types_new_json.rs) for more details. +* `Variant`, `Dynamic` types are not supported for now. See also the additional examples: diff --git a/examples/README.md b/examples/README.md index 35e4270..3f1cdaa 100644 --- a/examples/README.md +++ b/examples/README.md @@ -18,6 +18,7 @@ If something is missing, or you found a mistake in one of these examples, please - [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. +- [data_types_new_json.rs](data_types_new_json.rs) - working with the [new JSON data type](https://clickhouse.com/docs/en/sql-reference/data-types/newjson) as a String. ### Special cases diff --git a/examples/data_types_new_json.rs b/examples/data_types_new_json.rs new file mode 100644 index 0000000..0e50d77 --- /dev/null +++ b/examples/data_types_new_json.rs @@ -0,0 +1,75 @@ +use clickhouse_derive::Row; +use serde::{Deserialize, Serialize}; + +use clickhouse::sql::Identifier; +use clickhouse::{error::Result, Client}; + +// Requires ClickHouse 24.10+, as the `input_format_binary_read_json_as_string` and `output_format_binary_write_json_as_string` settings were added in that version. +// Inserting and selecting a row with a JSON column as a string. +// See also: https://clickhouse.com/docs/en/sql-reference/data-types/newjson + +#[tokio::main] +async fn main() -> Result<()> { + let table_name = "chrs_data_types_new_json"; + let client = Client::default() + .with_url("http://localhost:8123") + // All these settings can instead be applied on the query or insert level with the same `with_option` method. + // Enable new JSON type usage + .with_option("allow_experimental_json_type", "1") + // Enable inserting JSON columns as a string + .with_option("input_format_binary_read_json_as_string", "1") + // Enable selecting JSON columns as a string + .with_option("output_format_binary_write_json_as_string", "1"); + + client + .query( + " + CREATE OR REPLACE TABLE ? + ( + id UInt64, + data JSON + ) ENGINE MergeTree ORDER BY id; + ", + ) + .bind(Identifier(table_name)) + .execute() + .await?; + + let row = Row { + id: 1, + data: r#" + { + "name": "John Doe", + "age": 42, + "phones": [ + "+123 456 789", + "+987 654 321" + ] + }"# + .to_string(), + }; + + let mut insert = client.insert(table_name)?; + insert.write(&row).await?; + insert.end().await?; + + let db_row = client + .query("SELECT ?fields FROM ? LIMIT 1") + .bind(Identifier(table_name)) + .fetch_one::() + .await?; + + println!("{db_row:#?}"); + + // You can then use any JSON library to parse the JSON string, e.g., serde_json. + let json_value: serde_json::Value = serde_json::from_str(&db_row.data).expect("Invalid JSON"); + println!("Extracted name from JSON: {}", json_value["name"]); + + Ok(()) +} + +#[derive(Debug, Row, Serialize, Deserialize)] +pub struct Row { + id: u64, + data: String, +}