Skip to content

Commit 577ea49

Browse files
Merge pull request #1455 from ClickHouse/json_type
Add JSON Type
2 parents 75a2e2a + b123e14 commit 577ea49

18 files changed

+3364
-785
lines changed

chcol.go

+6
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import "github.com/ClickHouse/clickhouse-go/v2/lib/chcol"
2424
type (
2525
Variant = chcol.Variant
2626
Dynamic = chcol.Dynamic
27+
JSON = chcol.JSON
2728
)
2829

2930
// NewVariant creates a new Variant with the given value
@@ -45,3 +46,8 @@ func NewDynamic(v any) Dynamic {
4546
func NewDynamicWithType(v any, chType string) Dynamic {
4647
return chcol.NewDynamicWithType(v, chType)
4748
}
49+
50+
// NewJSON creates a new empty JSON value
51+
func NewJSON() *JSON {
52+
return chcol.NewJSON()
53+
}

examples/clickhouse_api/json_paths.go

+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
// Licensed to ClickHouse, Inc. under one or more contributor
2+
// license agreements. See the NOTICE file distributed with
3+
// this work for additional information regarding copyright
4+
// ownership. ClickHouse, Inc. licenses this file to you under
5+
// the Apache License, Version 2.0 (the "License"); you may
6+
// not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
package clickhouse_api
19+
20+
import (
21+
"context"
22+
"fmt"
23+
"github.com/ClickHouse/clickhouse-go/v2"
24+
"time"
25+
)
26+
27+
func JSONPathsExample() error {
28+
ctx := context.Background()
29+
30+
conn, err := GetNativeConnection(clickhouse.Settings{
31+
"allow_experimental_json_type": true,
32+
}, nil, nil)
33+
if err != nil {
34+
return err
35+
}
36+
37+
if !CheckMinServerVersion(conn, 24, 9, 0) {
38+
fmt.Print("unsupported clickhouse version for JSON type")
39+
return nil
40+
}
41+
42+
err = conn.Exec(ctx, "DROP TABLE IF EXISTS go_json_example")
43+
if err != nil {
44+
return err
45+
}
46+
47+
err = conn.Exec(ctx, `
48+
CREATE TABLE go_json_example (product JSON) ENGINE=Memory
49+
`)
50+
if err != nil {
51+
return err
52+
}
53+
54+
batch, err := conn.PrepareBatch(ctx, "INSERT INTO go_json_example (product)")
55+
if err != nil {
56+
return err
57+
}
58+
59+
insertProduct := clickhouse.NewJSON()
60+
insertProduct.SetValueAtPath("id", clickhouse.NewDynamicWithType(uint64(1234), "UInt64"))
61+
insertProduct.SetValueAtPath("name", "Book")
62+
insertProduct.SetValueAtPath("tags", []string{"library", "fiction"})
63+
insertProduct.SetValueAtPath("pricing.price", int64(750))
64+
insertProduct.SetValueAtPath("pricing.currency", "usd")
65+
insertProduct.SetValueAtPath("metadata.region", "us")
66+
insertProduct.SetValueAtPath("metadata.page_count", int64(852))
67+
insertProduct.SetValueAtPath("created_at", clickhouse.NewDynamicWithType(time.Now().UTC().Truncate(time.Millisecond), "DateTime64(3)"))
68+
69+
if err = batch.Append(insertProduct); err != nil {
70+
return err
71+
}
72+
73+
if err = batch.Send(); err != nil {
74+
return err
75+
}
76+
77+
var selectedProduct clickhouse.JSON
78+
79+
if err = conn.QueryRow(ctx, "SELECT product FROM go_json_example").Scan(&selectedProduct); err != nil {
80+
return err
81+
}
82+
83+
fmt.Printf("inserted product: %+v\n", insertProduct)
84+
fmt.Printf("selected product: %+v\n", selectedProduct)
85+
return nil
86+
}
+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
// Licensed to ClickHouse, Inc. under one or more contributor
2+
// license agreements. See the NOTICE file distributed with
3+
// this work for additional information regarding copyright
4+
// ownership. ClickHouse, Inc. licenses this file to you under
5+
// the Apache License, Version 2.0 (the "License"); you may
6+
// not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
package clickhouse_api
19+
20+
import (
21+
"context"
22+
"fmt"
23+
"github.com/ClickHouse/clickhouse-go/v2"
24+
)
25+
26+
func JSONStringExample() error {
27+
ctx := context.Background()
28+
29+
conn, err := GetNativeConnection(clickhouse.Settings{
30+
"allow_experimental_json_type": true,
31+
"output_format_native_write_json_as_string": true,
32+
}, nil, nil)
33+
if err != nil {
34+
return err
35+
}
36+
37+
if !CheckMinServerVersion(conn, 24, 9, 0) {
38+
fmt.Print("unsupported clickhouse version for JSON type")
39+
return nil
40+
}
41+
42+
err = conn.Exec(ctx, "DROP TABLE IF EXISTS go_json_example")
43+
if err != nil {
44+
return err
45+
}
46+
47+
err = conn.Exec(ctx, `
48+
CREATE TABLE go_json_example (product JSON) ENGINE=Memory
49+
`)
50+
if err != nil {
51+
return err
52+
}
53+
54+
batch, err := conn.PrepareBatch(ctx, "INSERT INTO go_json_example (product)")
55+
if err != nil {
56+
return err
57+
}
58+
59+
insertProductString := "{\"id\":1234,\"name\":\"Book\",\"tags\":[\"library\",\"fiction\"]," +
60+
"\"pricing\":{\"price\":750,\"currency\":\"usd\"},\"metadata\":{\"page_count\":852,\"region\":\"us\"}," +
61+
"\"created_at\":\"2024-12-19T11:20:04.146Z\"}"
62+
63+
if err = batch.Append(insertProductString); err != nil {
64+
return err
65+
}
66+
67+
if err = batch.Send(); err != nil {
68+
return err
69+
}
70+
71+
var selectedProductString string
72+
73+
if err = conn.QueryRow(ctx, "SELECT product FROM go_json_example").Scan(&selectedProductString); err != nil {
74+
return err
75+
}
76+
77+
fmt.Printf("inserted product string: %s\n", insertProductString)
78+
fmt.Printf("selected product string: %s\n", selectedProductString)
79+
fmt.Printf("inserted product string matches selected product string: %t\n", insertProductString == selectedProductString)
80+
return nil
81+
}
+121
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
// Licensed to ClickHouse, Inc. under one or more contributor
2+
// license agreements. See the NOTICE file distributed with
3+
// this work for additional information regarding copyright
4+
// ownership. ClickHouse, Inc. licenses this file to you under
5+
// the Apache License, Version 2.0 (the "License"); you may
6+
// not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
package clickhouse_api
19+
20+
import (
21+
"context"
22+
"encoding/json"
23+
"fmt"
24+
"github.com/ClickHouse/clickhouse-go/v2"
25+
"time"
26+
)
27+
28+
type ProductPricing struct {
29+
Price int64 `json:"price"`
30+
Currency string `json:"currency"`
31+
}
32+
33+
type Product struct {
34+
ID clickhouse.Dynamic `json:"id"`
35+
Name string `json:"name"`
36+
Tags []string `json:"tags"`
37+
Pricing ProductPricing `json:"pricing"`
38+
Metadata map[string]interface{} `json:"metadata"`
39+
CreatedAt time.Time `json:"created_at" chType:"DateTime64(3)"`
40+
}
41+
42+
func NewExampleProduct() *Product {
43+
return &Product{
44+
ID: clickhouse.NewDynamicWithType(uint64(1234), "UInt64"),
45+
Name: "Book",
46+
Tags: []string{"library", "fiction"},
47+
Pricing: ProductPricing{
48+
Price: 750,
49+
Currency: "usd",
50+
},
51+
Metadata: map[string]interface{}{
52+
"region": "us",
53+
"page_count": int64(852),
54+
},
55+
CreatedAt: time.Now().UTC().Truncate(time.Millisecond),
56+
}
57+
}
58+
59+
func JSONStructExample() error {
60+
ctx := context.Background()
61+
62+
conn, err := GetNativeConnection(clickhouse.Settings{
63+
"allow_experimental_json_type": true,
64+
}, nil, nil)
65+
if err != nil {
66+
return err
67+
}
68+
69+
if !CheckMinServerVersion(conn, 24, 9, 0) {
70+
fmt.Print("unsupported clickhouse version for JSON type")
71+
return nil
72+
}
73+
74+
err = conn.Exec(ctx, "DROP TABLE IF EXISTS go_json_example")
75+
if err != nil {
76+
return err
77+
}
78+
79+
err = conn.Exec(ctx, `
80+
CREATE TABLE go_json_example (product JSON) ENGINE=Memory
81+
`)
82+
if err != nil {
83+
return err
84+
}
85+
86+
batch, err := conn.PrepareBatch(ctx, "INSERT INTO go_json_example (product)")
87+
if err != nil {
88+
return err
89+
}
90+
91+
insertProduct := NewExampleProduct()
92+
93+
if err = batch.Append(insertProduct); err != nil {
94+
return err
95+
}
96+
97+
if err = batch.Send(); err != nil {
98+
return err
99+
}
100+
101+
var selectedProduct Product
102+
103+
if err = conn.QueryRow(ctx, "SELECT product FROM go_json_example").Scan(&selectedProduct); err != nil {
104+
return err
105+
}
106+
107+
insertProductBytes, err := json.Marshal(insertProduct)
108+
if err != nil {
109+
return err
110+
}
111+
112+
selectedProductBytes, err := json.Marshal(&selectedProduct)
113+
if err != nil {
114+
return err
115+
}
116+
117+
fmt.Printf("inserted product: %s\n", string(insertProductBytes))
118+
fmt.Printf("selected product: %s\n", string(selectedProductBytes))
119+
fmt.Printf("inserted product matches selected product: %t\n", string(insertProductBytes) == string(selectedProductBytes))
120+
return nil
121+
}

examples/clickhouse_api/main_test.go

+13
Original file line numberDiff line numberDiff line change
@@ -217,3 +217,16 @@ func TestVariantExample(t *testing.T) {
217217
func TestDynamicExample(t *testing.T) {
218218
require.NoError(t, DynamicExample())
219219
}
220+
221+
func TestJSONPathsExample(t *testing.T) {
222+
require.NoError(t, JSONPathsExample())
223+
}
224+
225+
func TestJSONStructExample(t *testing.T) {
226+
require.NoError(t, JSONStructExample())
227+
}
228+
229+
func TestJSONStringExample(t *testing.T) {
230+
t.Skip("client cannot receive JSON strings")
231+
require.NoError(t, JSONStringExample())
232+
}

0 commit comments

Comments
 (0)