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 withX-HTTP-Method-Override: GETand 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'squery_documentmethod.DocumentandSchemapath helpers: were producing paths likedocument/{org}/{db}— missing the/{repo}/branch/{branch}segment. All document and schema operations went to the default branch regardless ofconfig.branch, so documents inserted on a feature branch were visible on main. Bothdocument_path/2andschema_path/2now include repo and branch from config.Schema.all/2: returned the raw server response which includes an@contextkey (JSON-LD metadata). Now filters out@-prefixed keys soMap.keys/1returns only class names.Client.req_opts/1: added:headersto the allowed opts so per-request headers (e.g.X-HTTP-Method-Override) can be passed through to Req.- Livebook script (
terminusdb_ex_livebook.exsand.livemd): added missingemailfield to Dave and Eve inserts (schema requires it), replaced%Error{}struct pattern with map pattern (structs fromMix.installdeps aren't available at compile time in.exsscripts), wrappedDatabase.create!intry/rescueso cleanup runs, added delete-if-exists guard before database creation, updatedMix.installversion to~> 0.3.3.
Added — Regression tests
- Unit:
Document.querysends 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.allfilters@contextfrom response. - Unit:
Schema.all!returns frames without@context. - Integration:
Document.queryfilters by template fields (age: 28 → only Carol). - Integration: documents on feature branch not visible on main.
- Integration:
Schema.allreturns only class names, not@context.
[0.3.2] — 2026-06-26
Added — GraphQL (ADR-0009)
TerminusDB.GraphQLmodule: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.RDFListmodule 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.Prefixmodule:get/2,add/3,update/3,upsert/3,delete/2,all/2.Branch.squash/2,Branch.reset/3,Database.optimize/2.TerminusDB.Patchstruct: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.Triplesmodule:get/2,update/3,insert/3.WOQL.execute_stream/3— streaming WOQL results.TerminusDB.Remotemodule:clone/4,fetch/2,push/3,pull/3.
Added — Benchmarks
bencheedev dependency.TerminusDB.Benchmarkhelper 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/2changed tordflist_push/3: now takes anew_head_varparameter so callers can update their reference to the new list head.Benchmark.seed_database/2return type changed fromConfig.t()to{Config.t(), String.t()}so callers can clean up the database after benchmarking.Patch.before/1andPatch.update/1asymmetry documented:before/1preserves non-SwapValue fields for full state reconstruction;update/1only includes changed (SwapValue) fields.WOQL.execute_stream/3now uses lazyStream.mapinstead of eagerEnum.map, and safeJason.decode/1instead ofJason.decode!/1.GraphQL.query/3,GraphQL.mutate/3,GraphQL.introspect/2, and allPrefix.*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_nth1variable-index path: fixed missingv.decvariable in localize map; addedeval(minus(...))for decrement; unrolled recursion to avoid infinite Elixir recursion (C1).rdflist_slice/4: fixed silently ignoredend_valparameter; rewrote with proper start/end bounds using cell navigation + collect (C2).rdflist_swap/3: fixed no-op implementation; addedrdflist_cell_athelper and delete_triple/add_triple write operations for both cells (C3).rdflist_drop/2: fixed to operate on the cell atposition, 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 forrange_min/range_maxoperands (usesDataValuewrapper withlistfield).
Deferred to v0.3.3+
- GraphQL builder DSL.
graph/1context 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.Schemamacro).
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(aliasoptional/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(aliassubsumption/2),cast/3(aliastypecast/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(aliassubstring/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..4with 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(aliasidgenerator/3),idgen_random/2(aliasrandom_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, andArithmeticValue(matching the Python/JS clients), replacing the 2-wrapperNodeValue/DataValuemodel from v0.3. triple/3object encoding: constant string objects now encode asValuewithxsd:stringdata (literals), matching Python. Previously they encoded asNodeValuewithnode(IRIs). Migration: passiri("...")explicitly when an IRI object is intended.read_document/2field 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/2operands: now wrapped inValue(wasDataValue), matching Python.type_of/2:valueusesValue,typeusesValue(wasNodeValue).floatliterals: encode asxsd:decimalin the query builder (matches Python's wire output).- Module split:
woql.exnow delegates encoding/decoding/path/literal helpers to internal sub-modules (Encoder, Decoder, Path, Literal). - Version bumped to 0.3.1;
source_refupdated 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
ArgumentErroron empty or malformed patterns instead of crashing withMatchError. - Path quantifier
{n}now correctly producesPathTimeswithto = 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:configtoTerminusDB.Errorreason types.insert_document/2andupdate_document/2: document maps are now encoded asDictionaryTemplatewithFieldValuePairentries (matching the JSdoc()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/1context 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. Supportsbefore/afterdocument values or branch/commit refs, plus:keepfor field preservation.TerminusDB.Merge: branch merge (rebase) —merge/2(plus!/variant). Uses the/api/rebaseendpoint withauthor/rebase_frombody.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/DataValuewrappers, short type names like"Triple","Equals").to_jsonld/1andfrom_jsonld/1are round-trip tested.execute/3/execute!/3POST to/api/woql/:org/:db/:repo/branch/:branchwithcommit_infosupport.- Telemetry areas
:commitand:woqladded toTerminusDB.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 checkdb/:org/:db?branches=truebranch list (the/branchendpoint only supports POST/DELETE, not HEAD or GET).Document.query/3: now always sendsas_list=true(server returns concatenated JSON by default, which crashes Req's JSON decoder).Commit.history/2: uses the/logendpoint (the/historyendpoint requires a commit ID and cannot list without one).WOQL.eq/2: type is"Equals"not"Eq"; literals are wrapped inDataValuewith xsd type annotations.Merge.merge/2: uses/rebasewithauthor/rebase_frombody (not/pullwithremote/remote_branch, which causes 500 on local merges).WOQL.execute/3: now includes:repo/:branchin the WOQL path (woql/:org/:db/:repo/branch/:branch).
Changed
- Version bumped to 0.3.0;
source_refupdated for HexDocs. - TerminusDB 12 compatibility verified (v12.0.5): range queries, ISO8601 date
predicates, WOQL
comment/collectpredicates, cardinality onSet,@metadata/@contextJSON 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). Supportsgraph_type(instance/schema),author/messagecommit metadata,full_replace,raw_json,create,nuke, pagination (skip/count),as_list,unfold,minimized,compress_ids. Thestream/2function returns a lazyEnumerableof decoded documents with constant memory via Req'sinto: :selfand the concatenated-JSON splitter.TerminusDB.Schema: schema frame API —frame/3,all/2(plus!/variants). Supportscompress_idsandexpand_abstractparams (including explicitfalse).TerminusDB.Branch: branch management API —create/3,delete/3,exists?/3(plus!/variants). Supports:from,:organization,:repooverrides 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) anddocument_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, andas_listset tofalseare now sent to the server (previously silently dropped, so the server'struedefaults always won).Document.stream/2: raisesTerminusDB.Erroron client errors instead ofMatchError.Branch.create/3::organizationand:repooverrides 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/2signature changed from(org, db)to(config, opts).- Version bumped to 0.2.0;
source_refupdated for HexDocs. .sobelow-confignoresSQL.Queryfalse positive onDocument.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, andredact/1for safe logging.TerminusDB.Error: typed error struct + exception with:reason(:transport/:http/:api/:decode), structuredapi:*parsing, and constructorstransport/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 fakeadapter: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.ymlfor 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 viamix test --only integration.