# AshClickhouse
> **Note:** This library is under active development, unstable and the API may change.
An [Ash Framework](https://ash-hq.org) data layer for [ClickHouse](https://clickhouse.com). It implements the
`Ash.DataLayer` behaviour using the [`clickhouse`](https://hex.pm/packages/clickhouse) client.
## Features
ClickHouse is a full SQL columnar store, so most Ash query features map directly:
- CRUD (`create`, `read`, `update`, `destroy`)
- `filter`, `sort`, `limit`, `offset`, `select`, `distinct`
- `bulk_create` (batch `INSERT`)
- `update_query` / `destroy_query` (via `ALTER TABLE ... UPDATE/DELETE`)
- Native aggregates (`count`, `sum`, `avg`, `min`, `max`)
- `multitenancy` (database- or attribute-based)
- `calculate`, `composite_primary_key`, `nested_expressions`, `boolean_filter`
- `join`, `combine` (UNION/INTERSECT), `filter_relationship`, `exists`, `aggregate_relationship`
### Not supported
- `transact` / `lock` — ClickHouse has no multi-statement transactions
- `keyset` — ClickHouse has no token-based keyset pagination (use `offset`)
- `upsert` — ClickHouse has no `ON CONFLICT` (use `create` + `update_query`)
## Installation
```elixir
def deps do
[
{:ash_clickhouse, "~> 0.1.0"}
]
end
```
## Quick start
### 1. Configure a Repo
```elixir
defmodule MyApp.Repo do
use AshClickhouse.Repo, otp_app: :my_app
end
```
```elixir
# config/config.exs
config :my_app, MyApp.Repo,
url: "http://localhost:8123",
database: "my_app_dev"
```
### 2. Add to your supervision tree
```elixir
children = [MyApp.Repo, ...]
```
### 3. Define a resource
```elixir
defmodule MyApp.User do
use Ash.Resource,
data_layer: AshClickhouse.DataLayer,
domain: MyApp.Domain
clickhouse do
table "users"
repo MyApp.Repo
engine "MergeTree()"
order_by "id"
end
attributes do
uuid_primary_key :id
attribute :name, :string
attribute :email, :string
end
actions do
defaults [:create, :read, :update, :destroy]
end
end
```
### 4. Create the database and tables
```sh
mix ash_clickhouse.setup
mix ash_clickhouse.migrate
```
### 5. Use it
```elixir
{:ok, user} = Ash.create(MyApp.User, %{name: "John", email: "john@example.com"})
users = Ash.read!(MyApp.User)
```
## Configuration
### Resource (`clickhouse` DSL)
| Option | Description |
| --- | --- |
| `table` | Table name (overrides default) |
| `repo` | The `AshClickhouse.Repo` module |
| `database` | Database to use (overrides repo default) |
| `engine` | ClickHouse table engine (default `"MergeTree()"`) |
| `order_by` | `ORDER BY` expression for the engine |
| `partition_by` | `PARTITION BY` expression |
| `primary_key` | Explicit primary key columns |
| `settings` | Engine settings string |
| `base_filter` | Filter applied to all queries |
| `default_context` | Context merged into every query/changeset |
| `migrate` | Whether to include in migrations (default `true`) |
### Repo
| Option | Default |
| --- | --- |
| `url` | `"http://localhost:8123"` |
| `username` | `"default"` |
| `password` | `""` |
| `database` | `nil` |
| `timeout` | `30_000` |
## License
Apache-2.0. See [LICENSE](LICENSE).