All notable changes to terminusdb_ex are documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

[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

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 deletetriple 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.