AshClickhouse supports the standard Ash query API. Most features map directly to ClickHouse SQL.
Basic CRUD
# Create
{:ok, user} = Ash.create(MyApp.User, %{name: "John", email: "john@example.com"})
# Read
users = Ash.read!(MyApp.User)
# Update
Ash.update!(user, %{name: "Jane"})
# Destroy
Ash.destroy!(user)Filtering
Full WHERE support via Ash.Query / action filters:
MyApp.User
|> Ash.Query.filter(age > 18 and name == "John")
|> Ash.read!()Boolean expressions, nested expressions, and changeset_filter are supported.
Relationship filters (filter_relationship, unrelated exists,
aggregate_relationship) are not supported yet.
Sorting, limit, offset, distinct
MyApp.User
|> Ash.Query.sort(age: :desc)
|> Ash.Query.limit(10)
|> Ash.Query.offset(20)
|> Ash.Query.distinct(:name)
|> Ash.read!()Keyset pagination is not supported — use offset/limit.
Selecting fields
MyApp.User
|> Ash.Query.select([:id, :name])
|> Ash.read!()Aggregates
Native aggregates: count, sum, avg, min, max.
# Aggregate on a query
MyApp.User
|> Ash.Query.aggregate(:count, :id, :total)
|> Ash.read!()
# Aggregate defined on the resource
Ash.read!(MyApp.User, query: [aggregate: :total_count])aggregate_filter and aggregate_sort are not supported.
Bulk create
Ash.bulk_create!(
MyApp.User,
[%{name: "A"}, %{name: "B"}],
:create
)Bulk create is implemented as a batched INSERT (default batch size 1000,
max 100_000). Per-resource insert_opts (e.g. async_insert: 1) are applied.
Update / destroy queries
These map to ClickHouse ALTER TABLE ... UPDATE / DELETE mutations:
MyApp.User
|> Ash.Query.filter(age < 0)
|> Ash.update_query!(set: [age: 0])
MyApp.User
|> Ash.Query.filter(status: "deleted")
|> Ash.destroy_query!()Mutations run asynchronously by default; control this with the resource's
mutations_sync option (1 = wait on current replica, 2 = wait on all
replicas, nil = async).
Calculations
Calculations are computed in memory after fetching rows.
Streaming
stream is supported and yields rows from the result set.
Multitenancy in queries
See Multitenancy. Tenant is set via
Ash.Query.set_tenant/2 and applied as a database qualifier or a filter
depending on the strategy.