# Changelog
All notable changes to `terminusdb_ex` are documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [0.3.3] — 2026-06-27
### Fixed
- `Document.query/3`: was sending the template as a bare GET body instead of
a POST with `X-HTTP-Method-Override: GET` and the template wrapped in a
`{"query": template, "graph_type": ..., "skip": ...}` body. The server
ignored the unwrapped template and returned all documents. Now matches the
Python client's `query_document` method.
- `Document` and `Schema` path helpers: were producing paths like
`document/{org}/{db}` — missing the `/{repo}/branch/{branch}` segment.
All document and schema operations went to the default branch regardless
of `config.branch`, so documents inserted on a feature branch were visible
on main. Both `document_path/2` and `schema_path/2` now include repo and
branch from config.
- `Schema.all/2`: returned the raw server response which includes an
`@context` key (JSON-LD metadata). Now filters out `@`-prefixed keys so
`Map.keys/1` returns only class names.
- `Client.req_opts/1`: added `:headers` to the allowed opts so per-request
headers (e.g. `X-HTTP-Method-Override`) can be passed through to Req.
- Livebook script (`terminusdb_ex_livebook.exs` and `.livemd`): added missing
`email` field to Dave and Eve inserts (schema requires it), replaced
`%Error{}` struct pattern with map pattern (structs from `Mix.install` deps
aren't available at compile time in `.exs` scripts), wrapped
`Database.create!` in `try/rescue` so cleanup runs, added delete-if-exists
guard before database creation, updated `Mix.install` version to `~> 0.3.3`.
### Added — Regression tests
- Unit: `Document.query` sends POST with wrapped body and override header.
- Unit: document paths include repo/branch from config (feature branch test).
- Unit: document paths default to main when no branch scoped.
- Unit: `Schema.all` filters `@context` from response.
- Unit: `Schema.all!` returns frames without `@context`.
- Integration: `Document.query` filters by template fields (age: 28 → only Carol).
- Integration: documents on feature branch not visible on main.
- Integration: `Schema.all` returns only class names, not `@context`.
## [0.3.2] — 2026-06-26
### Added — GraphQL (ADR-0009)
- `TerminusDB.GraphQL` module: `query/3`, `mutate/3`, `introspect/2` — thin
HTTP wrapper for the `/api/graphql/{org}/{db}` endpoint.
### Added — Temporal / Allen WOQL (ADR-0010)
- `interval/3`, `interval_start_duration/3`, `interval_duration_end/3`,
`interval_relation/5`, `interval_relation_typed/3`, `date_duration/3`,
`day_after/2`, `day_before/2`, `weekday/2`, `weekday_sunday_start/2`,
`iso_week/3`, `month_start_date/2`, `month_end_date/2`,
`month_start_dates/3`, `month_end_dates/3`, `in_range/3`, `sequence/5`,
`range_min/2`, `range_max/2`.
### Added — RDF list library (ADR-0011)
- `TerminusDB.WOQL.RDFList` module with 17 functions: `rdflist_list/2`,
`rdflist_peek/2`, `rdflist_last/2`, `rdflist_nth0/3`, `rdflist_nth1/3`,
`rdflist_member/2`, `rdflist_length/2`, `rdflist_pop/2`, `rdflist_push/3`,
`rdflist_append/3`, `rdflist_clear/2`, `rdflist_empty/1`,
`rdflist_is_empty/1`, `rdflist_slice/4`, `rdflist_insert/4`,
`rdflist_drop/2`, `rdflist_swap/3`.
### Added — CSV / IO (ADR-0012)
- `WOQL.get/2`, `WOQL.put/3`, `WOQL.woql_as/1`, `WOQL.file/2`,
`WOQL.remote/2`, `WOQL.post/2`.
### Added — Range queries
- `triple_slice/5`, `quad_slice/6`, `triple_slice_rev/5`,
`quad_slice_rev/6`, `triple_next/4`, `quad_next/5`,
`triple_previous/4`, `quad_previous/5`.
### Added — Client API gaps
- `TerminusDB.Prefix` module: `get/2`, `add/3`, `update/3`, `upsert/3`,
`delete/2`, `all/2`.
- `Branch.squash/2`, `Branch.reset/3`, `Database.optimize/2`.
- `TerminusDB.Patch` struct: `from_json/1`, `to_json/1`, `update/1`,
`before/1`, `copy/1`.
- `Diff.diff_object/2`, `Diff.diff_version/2`, `Diff.patch/2`,
`Diff.patch_resource/2`, `Diff.apply/3`.
- `Commit.document_history/3`.
- `TerminusDB.Triples` module: `get/2`, `update/3`, `insert/3`.
- `WOQL.execute_stream/3` — streaming WOQL results.
- `TerminusDB.Remote` module: `clone/4`, `fetch/2`, `push/3`, `pull/3`.
### Added — Benchmarks
- `benchee` dev dependency.
- `TerminusDB.Benchmark` helper module.
- 5 benchmark suites in `bench/`.
### Added — Tutorials
- `guides/graphql-guide.md` — GraphQL queries, mutations, filters, pagination.
- `guides/temporal-allen-guide.md` — Intervals, Allen relations, calendar ops.
- `guides/csv-import-guide.md` — CSV reading/writing with WOQL.
- `guides/rdf-list-guide.md` — RDF list manipulation.
### Changed
- `rdflist_push/2` changed to `rdflist_push/3`: now takes a `new_head_var`
parameter so callers can update their reference to the new list head.
- `Benchmark.seed_database/2` return type changed from `Config.t()` to
`{Config.t(), String.t()}` so callers can clean up the database after
benchmarking.
- `Patch.before/1` and `Patch.update/1` asymmetry documented: `before/1`
preserves non-SwapValue fields for full state reconstruction; `update/1`
only includes changed (SwapValue) fields.
- `WOQL.execute_stream/3` now uses lazy `Stream.map` instead of eager
`Enum.map`, and safe `Jason.decode/1` instead of `Jason.decode!/1`.
- `GraphQL.query/3`, `GraphQL.mutate/3`, `GraphQL.introspect/2`, and all
`Prefix.*` functions now return `{:error, %Error{reason: :config}}` when
no database is scoped, instead of raising.
### Fixed — Review fixes (review-0.3.2.md)
- `rdflist_nth0`/`rdflist_nth1` variable-index path: fixed missing `v.dec`
variable in localize map; added `eval(minus(...))` for decrement; unrolled
recursion to avoid infinite Elixir recursion (C1).
- `rdflist_slice/4`: fixed silently ignored `end_val` parameter; rewrote
with proper start/end bounds using cell navigation + collect (C2).
- `rdflist_swap/3`: fixed no-op implementation; added `rdflist_cell_at`
helper and delete_triple/add_triple write operations for both cells (C3).
- `rdflist_drop/2`: fixed to operate on the cell at `position`, not the list
head; position 0 deletes from head, position > 0 navigates to cell and
relinks parent (H1).
- `rdflist_clear/2`: wrapped delete_triple calls inside `opt(and_([triple,
delete]))` blocks to guard against unbound variables (M4).
- `encode_arithmetic/1`: added clause for non-variable binary strings (L2).
- `Benchmark.seed_database/2`: returns `{config, db_name}` for cleanup (L4).
- RDFList tests: added structural AST assertions verifying write operations
(AddTriple/DeleteTriple) in swap/drop, dec variable in nth, Optional
blocks in clear (L5).
- `encode_value/1`: added list encoding for `range_min`/`range_max`
operands (uses `DataValue` wrapper with `list` field).
### Deferred to v0.3.3+
- GraphQL builder DSL.
- `graph/1` context setter.
- Data version headers (`last_data_version`/`get_data_version`).
- Gzip compression for large document inserts.
- Macro sugar layer (`TerminusDB.WOQL.Macros`).
### Deferred to v0.4+
- Access control (organizations, users, roles, capabilities).
- DataFrame (Explorer) integration.
- Ecto integration (`TerminusDB.Schema` macro).
## [0.3.1] — 2026-06-25
### Added — WOQL DSL v0.2 (ADR-0008)
Expanded the WOQL builder DSL from 7 operators to ~70, covering the core and
important-advanced vocabulary (Tier 1+2).
- **Logical combinators:** `not_/1`, `opt/1` (alias `optional/1`), `once/1`,
`immediately/1`.
- **Query modifiers:** `distinct/2`, `limit/2`, `start/2`, `order_by/2` (accepts
tuple-list or keyword-list form), `group_by/4`, `count/2`, `collect/3`, `star/0`,
`all/0`.
- **Graph patterns:** `quad/4`, `added_triple/3`, `removed_triple/3`, `added_quad/4`,
`removed_quad/4`, `add_triple/3`, `delete_triple/3`, `add_quad/4`, `delete_quad/4`,
`update_triple/3`, `update_quad/4`.
- **Comparison:** `less/2`, `greater/2`, `gte/2`, `lte/2`, `like/3`.
- **Schema ops:** `isa/2`, `sub/2` (alias `subsumption/2`), `cast/3` (alias
`typecast/3`).
- **Arithmetic:** `eval/2`, `plus/1`, `minus/1`, `times/1`, `divide/1`, `div/1`,
`exp/2`, `floor/1`, `sum/2`.
- **String ops:** `concat/2`, `join/3`, `substr/5` (alias `substring/5`), `trim/2`,
`upper/2`, `lower/2`, `pad/4`, `split/3`, `length/2`, `regexp/3`.
- **List/Set/Dict:** `dot/3`, `member/2`, `slice/4`, `set_difference/3`,
`set_intersection/3`, `set_union/3`, `set_member/2`, `list_to_set/2`.
- **Path/navigation:** `path/3..4` with a dual-mode DSL — string-compiled parser
(`path("v:S", "<friend*{1,3}", "v:O")`) and structured builders (`path_star/1`,
`path_plus/1`, `path_times/3`, `path_seq/1`, `path_or/1`, `path_inverse/1`,
`path_pred/1`, `path_any/0`).
- **ID generation:** `unique/3`, `idgen/3` (alias `idgenerator/3`), `idgen_random/2`
(alias `random_idgen/2`).
- **Documents:** `insert_document/2`, `update_document/2` (optional identifier),
`delete_document/1`.
- **Graph context:** `using/2`, `from/2`, `into/2`, `comment/2`.
- **Graph meta:** `size/2`, `triple_count/2`.
- **Literal/value helpers:** `var/1`, `iri/1`, `string/1`, `boolean/1`, `datetime/1`,
`date/1`, `literal/2`, `true_/0`.
### Changed
- **4-wrapper value model:** the JSON-LD encoder now uses `NodeValue`, `Value`,
`DataValue`, and `ArithmeticValue` (matching the Python/JS clients), replacing the
2-wrapper `NodeValue`/`DataValue` model from v0.3.
- **`triple/3` object encoding:** constant string objects now encode as `Value` with
`xsd:string` data (literals), matching Python. Previously they encoded as `NodeValue`
with `node` (IRIs). **Migration:** pass `iri("...")` explicitly when an IRI object
is intended.
- **`read_document/2` field ordering:** the document id is now under `"identifier"`
(NodeValue) and the output variable under `"document"` (Value), matching the
canonical wire format. Previously these were swapped.
- **`eq/2` operands:** now wrapped in `Value` (was `DataValue`), matching Python.
- **`type_of/2`:** `value` uses `Value`, `type` uses `Value` (was `NodeValue`).
- **`float` literals:** encode as `xsd:decimal` in the query builder (matches Python's
wire output).
- Module split: `woql.ex` now delegates encoding/decoding/path/literal helpers to
internal sub-modules (Encoder, Decoder, Path, Literal).
- Version bumped to 0.3.1; `source_ref` updated for HexDocs.
### Fixed
- `read_document/2`: field ordering now matches the canonical WOQL JSON-LD wire format
(was reversed, causing incorrect encoding).
- Path parser: now raises `ArgumentError` on empty or malformed patterns instead
of crashing with `MatchError`.
- Path quantifier `{n}` now correctly produces `PathTimes` with `to = n` (exactly n),
distinguishing it from `{n,}` (at least n, unbounded).
- `WOQL.execute/3`: now returns `{:error, %Error{reason: :config}}` when no database
is scoped in config, instead of raising. Added `:config` to `TerminusDB.Error`
reason types.
- `insert_document/2` and `update_document/2`: document maps are now encoded as
`DictionaryTemplate` with `FieldValuePair` entries (matching the JS `doc()`
wrapper), fixing "Not well formed WOQL JSON-LD" errors on TerminusDB 12.
### Deferred to v0.3.2+
- Temporal / Allen interval algebra family (19 operators).
- RDF list library (`WOQLLibrary.rdflist_*`, 17 macros).
- CSV/IO (`get`, `put`, `woql_as`, `file`, `remote`, `post`).
- `graph/1` context setter.
- Macro sugar layer (`TerminusDB.WOQL.Macros`).
- Range query family (`triple_slice*`, `triple_next`, `triple_previous`).
## [0.3.0] — 2026-06-25
### Added — versioned query workflows and WOQL DSL
- **`TerminusDB.Commit`**: commit history and inspection — `log/2`, `history/2`,
`get/3` (plus `!/` variants). Branch-aware with pagination (`:start`, `:limit`).
- **`TerminusDB.Diff`**: document and branch-level diff — `compare/2` /
`compare!/2`. Supports `before`/`after` document values or branch/commit refs,
plus `:keep` for field preservation.
- **`TerminusDB.Merge`**: branch merge (rebase) — `merge/2` (plus
`!/` variant). Uses the `/api/rebase` endpoint with `author`/`rebase_from`
body.
- **`TerminusDB.WOQL`**: functional builder DSL (ADR-0002) — `triple/3`,
`and_/1`, `or_/1`, `eq/2`, `select/2`, `read_document/2`, `type_of/2`.
Serializes to the correct WOQL JSON-LD wire format (`NodeValue`/`DataValue`
wrappers, short type names like `"Triple"`, `"Equals"`). `to_jsonld/1` and
`from_jsonld/1` are round-trip tested. `execute/3` / `execute!/3` POST to
`/api/woql/:org/:db/:repo/branch/:branch` with `commit_info` support.
- Telemetry areas `:commit` and `:woql` added to `TerminusDB.Telemetry`.
- Integration tests for commit log/history, diff, merge (branch divergence +
rebase), and WOQL execution against a live TerminusDB 12.
- Overview guide updated with Commit, Diff, Merge, and WOQL DSL sections.
- 58 new unit tests (Commit, Diff, Merge, WOQL including round-trips).
### Fixed (discovered via integration testing against TerminusDB 12)
- `Schema.frame/3`: class name is now a `?type=` query param, not a path
segment (server returns 404 for path-appended class names).
- `Branch.exists?/3`: rewrote to check `db/:org/:db?branches=true` branch list
(the `/branch` endpoint only supports POST/DELETE, not HEAD or GET).
- `Document.query/3`: now always sends `as_list=true` (server returns
concatenated JSON by default, which crashes Req's JSON decoder).
- `Commit.history/2`: uses the `/log` endpoint (the `/history` endpoint requires
a commit ID and cannot list without one).
- `WOQL.eq/2`: type is `"Equals"` not `"Eq"`; literals are wrapped in
`DataValue` with xsd type annotations.
- `Merge.merge/2`: uses `/rebase` with `author`/`rebase_from` body (not
`/pull` with `remote`/`remote_branch`, which causes 500 on local merges).
- `WOQL.execute/3`: now includes `:repo`/`:branch` in the WOQL path
(`woql/:org/:db/:repo/branch/:branch`).
### Changed
- Version bumped to 0.3.0; `source_ref` updated for HexDocs.
- TerminusDB 12 compatibility verified (v12.0.5): range queries, ISO8601 date
predicates, WOQL `comment`/`collect` predicates, cardinality on `Set`,
`@metadata`/`@context` JSON handling, diff+streaming in history endpoint.
## [0.2.0] — 2026-06-24
### Added — document, schema, branch, and streaming APIs
- **`TerminusDB.Document`**: document CRUD and query API — `insert/3`, `get/2`,
`query/3`, `replace/3`, `delete/2`, `stream/2` (plus `!/` variants). Supports
`graph_type` (instance/schema), `author`/`message` commit metadata,
`full_replace`, `raw_json`, `create`, `nuke`, pagination (`skip`/`count`),
`as_list`, `unfold`, `minimized`, `compress_ids`. The `stream/2` function
returns a lazy `Enumerable` of decoded documents with constant memory via
Req's `into: :self` and the concatenated-JSON splitter.
- **`TerminusDB.Schema`**: schema frame API — `frame/3`, `all/2` (plus `!/`
variants). Supports `compress_ids` and `expand_abstract` params (including
explicit `false`).
- **`TerminusDB.Branch`**: branch management API — `create/3`, `delete/3`,
`exists?/3` (plus `!/` variants). Supports `:from`, `:organization`, `:repo`
overrides in both the path and the origin body.
- **`TerminusDB.Streaming`**: incremental concatenated-JSON decoder for streaming
document responses (ADR-0007). `split_concatenated/1` (bracket/depth-aware
splitter respecting string literals, escapes, and cross-chunk boundaries) and
`document_stream/2` (with a configurable receive timeout).
- Internal shared helper for building query parameters (`Client.Params`),
parameters, distinguishing flag params (omit when false) from tri-state bool
params (send explicit false to override server defaults).
- **`TerminusDB.Client.resource_path/2`**: now resolves org/db from config and
opts, used by all per-module path builders.
- Integration tests for Document (insert/query/stream/delete), Schema (frame
retrieval), and Branch (create/exists?/delete) against a Dockerized TerminusDB.
- 54 new unit tests covering Document, Schema, Branch, and Streaming.
- Guides: `guides/introduction.md` (TerminusDB concepts), `guides/migrating-from-sql.md`
(SQL-to-TerminusDB migration by example), `guides/overview.md` (feature walkthrough),
`guides/terminusdb_ex_livebook.livemd` (full Livebook demo).
- Hermetic doctest examples on every public function (27 doctests total).
### Fixed
- `Document.get/2`: `unfold`, `minimized`, `compress_ids`, and `as_list` set to
`false` are now sent to the server (previously silently dropped, so the
server's `true` defaults always won).
- `Document.stream/2`: raises `TerminusDB.Error` on client errors instead of
`MatchError`.
- `Branch.create/3`: `:organization` and `:repo` overrides are now reflected in
the origin body, not just the path.
- `Streaming.document_stream/2`: no longer hangs if the server never sends
`:done` — a receive timeout halts the stream.
- `Streaming.split_concatenated/1`: handles a lone trailing `\` at a chunk
boundary inside a string (retains it for the next chunk).
- Flaky telemetry test fixed (unique-path filtered `refute_receive`).
- All documentation examples corrected: `{:ok, _} = delete(...)` instead of
`:ok = delete(...)`.
### Changed
- `Client.resource_path/2` signature changed from `(org, db)` to `(config, opts)`.
- Version bumped to 0.2.0; `source_ref` updated for HexDocs.
- `.sobelow-conf` ignores `SQL.Query` false positive on `Document.query/3`.
## [0.1.0] — 2026-06-23
### Added — foundation (v0.1)
- **`TerminusDB.Config`**: immutable, NimbleOptions-validated connection/resource
context with scoping helpers (`with_database/2`, `with_branch/2`,
`with_organization/2`, `with_repo/2`, `with_ref/2`), Basic + Bearer auth, and
`redact/1` for safe logging.
- **`TerminusDB.Error`**: typed error struct + exception with `:reason`
(`:transport`/`:http`/`:api`/`:decode`), structured `api:*` parsing, and
constructors `transport/1`, `http/2`, `api/2`, `decode/2`.
- **`TerminusDB.Client`**: the single HTTP wire module (Req-based). `request/4`,
`request!/4`, `request_response/4`; centralizes auth, headers, JSON, error
mapping, and telemetry. Supports the Req fake `adapter:` for hermetic tests.
- **`TerminusDB.Database`**: database management API — `create/3`, `delete/3`,
`info/3`, `list/2`, `exists?/3`, `update/3` (plus `!/` variants).
- **`TerminusDB.Telemetry`**: `[:terminusdb, <area>, :start|:stop]` events with
measurements and redacted metadata.
- Telemetry on every operation; retry disabled for predictable behavior.
### Tooling & infrastructure
- Dependencies: `req`, `jason`, `nimble_options`, `telemetry`.
- Dev/test: `ex_doc`, `credo`, `dialyxir`, `sobelow`, `excoveralls`, `stream_data`.
- CI: GitHub Actions (format, compile, credo, sobelow, dialyzer, tests + coverage,
docs) on Elixir 1.18 / 1.19 / 1.20.
- Release workflow: tagged publishes to Hex.pm with a full quality gate.
- `docker-compose.yml` for local integration tests.
- Strict `.credo.exs`, formatter config (line length 98), dialyzer PLT caching.
### Documentation
- `ARCHITECTURE.md`: review summary, architecture option analysis, high-level
design.
- 7 ADRs (`docs/adr/`): Req, WOQL DSL, Ecto, ExDatalog, Telemetry, Testing,
Streaming.
- `AGENTS.md`: operating guide, commands, conventions, milestone roadmap.
- `LICENSE` (Apache-2.0), `CHANGELOG.md`, `README.md`.
### Tests
- 79 unit tests + 18 doctests, all hermetic (fake Req adapter). 100% `lib/`
coverage.
- Integration tests (`test/integration/`) against a Dockerized TerminusDB; run
manually via `mix test --only integration`.
[0.1.0]: https://github.com/thanos/terminusdb-client-elixir/releases/tag/v0.1.0
[0.2.0]: https://github.com/thanos/terminusdb-client-elixir/releases/tag/v0.2.0
[0.3.0]: https://github.com/thanos/terminusdb-client-elixir/releases/tag/v0.3.0
[0.3.1]: https://github.com/thanos/terminusdb-client-elixir/releases/tag/v0.3.1