Skip to content

Commit 5dc80ba

Browse files
authored
update Ch to add JSON support (#233)
* text json * support casting * add insert_all to the test * add more involved test * try again * use released ch * deps.get * eh * eh * update changelog * enable_json_type: 1
1 parent c7ef5bb commit 5dc80ba

File tree

5 files changed

+105
-2
lines changed

5 files changed

+105
-2
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changelog
22

3+
## Unreleased
4+
5+
- update Ch to [v0.5.x](https://github.com/plausible/ch/blob/master/CHANGELOG.md#050-2025-07-17) which adds Time, Variant, and JSON support https://github.com/plausible/ecto_ch/pull/233
6+
37
## 0.7.1 (2025-07-07)
48

59
- update [Ch](https://github.com/plausible/ch) (our ClickHouse client) to v0.4.x

mix.exs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ defmodule EctoCh.MixProject do
3939
# Run "mix help deps" to learn about dependencies.
4040
defp deps do
4141
[
42-
{:ch, "~> 0.4.0"},
42+
{:ch, "~> 0.5.0"},
4343
{:ecto_sql, "~> 3.13.0"},
4444
{:benchee, "~> 1.1", only: :bench},
4545
{:dialyxir, "~> 1.2", only: [:dev, :test], runtime: false},

mix.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
%{
22
"benchee": {:hex, :benchee, "1.4.0", "9f1f96a30ac80bab94faad644b39a9031d5632e517416a8ab0a6b0ac4df124ce", [:mix], [{:deep_merge, "~> 1.0", [hex: :deep_merge, repo: "hexpm", optional: false]}, {:statistex, "~> 1.0", [hex: :statistex, repo: "hexpm", optional: false]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "299cd10dd8ce51c9ea3ddb74bb150f93d25e968f93e4c1fa31698a8e4fa5d715"},
3-
"ch": {:hex, :ch, "0.4.1", "716fc326a0d29212a35c15e5350355550ff1290a244e6f37bf3eac4318aeeb76", [:mix], [{:db_connection, "~> 2.0", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:ecto, "~> 3.13.0", [hex: :ecto, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mint, "~> 1.0", [hex: :mint, repo: "hexpm", optional: false]}], "hexpm", "e6a4cf90d22030afde77e1a2895ebba2888032ccc5f1bec947b26b90a9152ffc"},
3+
"ch": {:hex, :ch, "0.5.0", "e1047b9a650d34ff5b001a149aa88ebfdf3d31a481e3e255747cf1635e9d54eb", [:mix], [{:db_connection, "~> 2.0", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:ecto, "~> 3.13.0", [hex: :ecto, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mint, "~> 1.0", [hex: :mint, repo: "hexpm", optional: false]}], "hexpm", "119100210a128fc4d5ef4e7531a1256f4ee686afc85241392e93bb1da0b56967"},
44
"db_connection": {:hex, :db_connection, "2.8.0", "64fd82cfa6d8e25ec6660cea73e92a4cbc6a18b31343910427b702838c4b33b2", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "008399dae5eee1bf5caa6e86d204dcb44242c82b1ed5e22c881f2c34da201b15"},
55
"decimal": {:hex, :decimal, "2.3.0", "3ad6255aa77b4a3c4f818171b12d237500e63525c2fd056699967a3e7ea20f62", [:mix], [], "hexpm", "a4d66355cb29cb47c3cf30e71329e58361cfcb37c34235ef3bf1d7bf3773aeac"},
66
"deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm", "ce708e5f094b9cd4e8f2be4f00d2f4250c4095be93f8cd6d018c753894885430"},

test/ecto/integration/json_test.exs

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
defmodule Ecto.Integration.JsonTest do
22
use Ecto.Integration.Case
3+
import Ecto.Query, only: [from: 2]
4+
5+
@moduletag :json
36

47
alias Ecto.Integration.TestRepo
58
alias EctoClickHouse.Integration.Setting
@@ -23,4 +26,99 @@ defmodule Ecto.Integration.JsonTest do
2326
[setting.id]
2427
)
2528
end
29+
30+
defmodule SemiStructured do
31+
use Ecto.Schema
32+
33+
@primary_key false
34+
schema "semi_structured" do
35+
field :json, Ch, type: "JSON"
36+
field :time, :naive_datetime
37+
end
38+
end
39+
40+
test "basic" do
41+
TestRepo.query!("""
42+
CREATE TABLE semi_structured (
43+
json JSON,
44+
time DateTime
45+
) ENGINE MergeTree ORDER BY time
46+
""")
47+
48+
on_exit(fn -> TestRepo.query!("DROP TABLE semi_structured") end)
49+
50+
%SemiStructured{}
51+
|> Ecto.Changeset.cast(
52+
%{
53+
json: %{"from" => "insert"},
54+
time: ~N[2023-10-01 12:00:00]
55+
},
56+
[:json, :time]
57+
)
58+
|> TestRepo.insert!()
59+
60+
TestRepo.insert_all(SemiStructured, [
61+
%{json: %{"from" => "insert_all"}, time: ~N[2023-10-01 13:00:00]},
62+
%{json: %{"from" => "another_insert_all"}, time: ~N[2023-10-01 13:01:00]}
63+
])
64+
65+
assert TestRepo.all(from s in SemiStructured, select: s.json, order_by: s.time) == [
66+
%{"from" => "insert"},
67+
%{"from" => "insert_all"},
68+
%{"from" => "another_insert_all"}
69+
]
70+
end
71+
72+
# https://github.com/plausible/ecto_ch/pull/233#issuecomment-3079317842
73+
74+
defmodule TokenInfoSchema do
75+
@moduledoc false
76+
use Ecto.Schema
77+
78+
@primary_key false
79+
schema "token_infos" do
80+
field :mint, Ch, type: "String"
81+
field :data, Ch, type: "JSON", source: :data
82+
field :created_at, Ch, type: "DateTime"
83+
end
84+
end
85+
86+
test "token_info_schema" do
87+
TestRepo.query!("""
88+
create table token_infos(
89+
mint String,
90+
data JSON,
91+
created_at DateTime
92+
) engine = MergeTree order by created_at
93+
""")
94+
95+
on_exit(fn -> TestRepo.query!("DROP TABLE token_infos") end)
96+
97+
missing_tokens = [
98+
%{
99+
mint: "123",
100+
data: %{"name" => "Test", "nested" => %{"name" => "Test", "arr" => ["abc", "b=deb"]}},
101+
created_at: NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second)
102+
},
103+
%{
104+
mint: "325",
105+
data: %{"name" => "Test", "nested" => %{"name" => "Test", "arr" => ["abc", "b=deb"]}},
106+
created_at: NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second)
107+
}
108+
]
109+
110+
assert {2, nil} = TestRepo.insert_all(TokenInfoSchema, missing_tokens)
111+
112+
assert TestRepo.all(
113+
from t in TokenInfoSchema,
114+
order_by: t.created_at,
115+
select: %{
116+
mint: t.mint,
117+
name: fragment("?.nested.name::text", t.data)
118+
}
119+
) == [
120+
%{mint: "123", name: "Test"},
121+
%{mint: "325", name: "Test"}
122+
]
123+
end
26124
end

test/test_helper.exs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ alias Ecto.Integration.TestRepo
2828
Application.put_env(:ecto_ch, TestRepo,
2929
adapter: Ecto.Adapters.ClickHouse,
3030
database: "ecto_ch_test",
31+
settings: [enable_json_type: 1],
3132
show_sensitive_data_on_connection_error: true
3233
)
3334

0 commit comments

Comments
 (0)