-
Notifications
You must be signed in to change notification settings - Fork 14
Support new JSON #245
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
Support new JSON #245
Conversation
|
@ruslandoga any development here? |
|
Thank you for reminding me about this one! I couldn't come up with a good solution right away and kind of put it off for later until Plausible or someone else asks for JSON support :) I guess that time has come! I'll try to finish it this weekend. Initially with
Feel free to share the problems you have in this thread, I would be happy to change ecto-ch to make working with JSON easier :) |
|
@ruslandoga thanks for the reply My issue might be just lack of familiarity with the library. Right now I have something like: defp filter(query, path, value) do
query
|> where([e], fragment("?.?", e.my_json_field, literal(^path)) == ^value)
endI don't know if there's a better approach without using a literal, this works but seems a bit hackish 😅 |
|
Have you tried json_extract_path? ecto_ch implements this Ecto operation with JSON_QUERY I wonder if that would work: defp filter(query, path, value) do
query
|> where([e], json_extract_path(e.my_json_field, ^path) == ^value) # or ^[path] or [^path]
endI'll try adding some tests around this use-case into ecto_ch to see how well it works tomorrow. |
|
I'm getting: iex(6)> ClickhouseRepo.all(Demo.Entry |> where([e], json_extract_path(e.my_json_field, ^["foo", "bar"]) == "5"))
** (UndefinedFunctionError) function Ch.type/0 is undefined or private. Did you mean:
* type/1
(ch 0.3.1) Ch.type()
(elixir 1.18.3) lib/enum.ex:1840: Enum."-map_reduce/3-lists^mapfoldl/2-0-"/3
(elixir 1.18.3) lib/enum.ex:2546: Enum."-reduce/3-lists^foldl/2-0-"/3
(elixir 1.18.3) lib/enum.ex:2546: Enum."-reduce/3-lists^foldl/2-0-"/3
(ecto 3.12.5) lib/ecto/repo/queryable.ex:214: Ecto.Repo.Queryable.execute/4
(ecto 3.12.5) lib/ecto/repo/queryable.ex:19: Ecto.Repo.Queryable.all/3In any case, JSON_QUERY doesn't seem compatible with the new json type. For the query to work it needs to be: |
This approach didn't work out quite as I had expected since we might not have access to those setting options if they were passed into Ch.query!(
conn,
"SELECT json FROM test",
_no_params = [],
settings: [
output_format_binary_write_json_as_string: 1
]
)
Repo.all(Schema, settings: [output_format_binary_write_json_as_string: 1]) |
|
It would be great to to have support for the native JSON type (https://clickhouse.com/docs/sql-reference/data-types/newjson), this would simply some of the usages |
|
Hey! Any updates on this PR? |
|
👋 Sorry for the delay! I think this PR wouldn't work as is, and since I don't want to push the decision of binary vs text JSON to the users, I think the "right" way is to implement binary JSON encoding/decoding. I plan on attempting it today after taking care of some other easier issues :) WIP: #261 |
|
👋 I created a branch that simplifies this PR by hard-coding the assumption of "json as text" while #261 is being worked on: plausible/ecto_ch#233 -- it uses #262 "underneath" |
|
👋 https://hex.pm/packages/ch/0.5.0 and https://hex.pm/packages/ecto_ch/0.8.0 now have basic JSON (as text) support. The Demos: Mix.install [{:ch, "~> 0.5.0"}]
{:ok, pid} = Ch.start_link()
[[%{}]] = Ch.query!(pid, ~s|select '{"a":null}'::JSON|).rows
[[%{"a" => "b"}]] = Ch.query!(pid, ~s|select '{"a":"b"}'::JSON|).rows
# note 42 becomes a string since arrays need to have the same type
[[%{"a" => ["b", "42"]}]] = Ch.query!(pid, ~s|select '{"a":["b", 42]}'::JSON|).rows
Ch.query!(pid, "create table ch_json(data JSON) engine Memory")
Ch.query!(pid, "insert into ch_json(data) format RowBinary", [[%{}], [nil], [%{"a" => 42}]], types: ["JSON"]).num_rows
[[%{}], [%{}], [%{"a" => "42"}]] = Ch.query!(pid, "select * from ch_json").rowsMix.install [{:ecto_ch, "~> 0.8.0"}]
defmodule Repo do
use Ecto.Repo, otp_app: :demo, adapter: Ecto.Adapters.ClickHouse
end
Repo.start_link()
Repo.query!("select version()").rows
#=> [["25.6.3.116"]]
# note 42 becomes a string here as well for some reason (that's how ClickHouse decides to encode it when output_format_binary_write_json_as_string)
[[%{"a" => "42"}]] = Repo.query!(~s|select '{"a":42}'::JSON|).rows
defmodule Event do
use Ecto.Schema
@primary_key false
schema "ecto_ch_json" do
field :data, Ch, type: "JSON"
end
end
Repo.query!("create table ecto_ch_json(data JSON) engine Memory")
Repo.insert_all(Event, [%{data: %{"a" => 42}}, %{data: nil}, %{data: %{}}, %{data: %{a: [1, 2, 3]}}])
import Ecto.Query
Repo.all(from e in Event, select: fragment("?.a::text", e.data))
#=> ["42", "", "", "[1,2,3]"] |
Closes #218
Somewhat depends on the answer to ClickHouse/ClickHouse#75768.
Rust client went the easy way: ClickHouse/clickhouse-rs#171 -- guess we can do that too (#246).