From 07284ad6b5916074505b5b990246022f1456266c Mon Sep 17 00:00:00 2001 From: radar roark <122068506+radarroark@users.noreply.github.com> Date: Sat, 10 Jan 2026 16:03:30 -0500 Subject: [PATCH 01/11] move lazy-seq check from primitive-for to v->slot! --- src/xitdb/util/conversion.clj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/xitdb/util/conversion.clj b/src/xitdb/util/conversion.clj index b450faa..22c10b6 100644 --- a/src/xitdb/util/conversion.clj +++ b/src/xitdb/util/conversion.clj @@ -104,9 +104,6 @@ [v] (cond - (validation/lazy-seq? v) - (throw (IllegalArgumentException. "Lazy sequences can be infinite and not allowed!")) - (string? v) (database-bytes v) @@ -147,6 +144,9 @@ [^WriteCursor cursor v] (cond + (validation/lazy-seq? v) + (throw (IllegalArgumentException. "Lazy sequences can be infinite and not allowed!")) + (instance? WriteArrayList v) (-> ^WriteArrayList v .cursor .slot) From 57ad2a949930997f96b467b463f9be726ee7f547 Mon Sep 17 00:00:00 2001 From: radar roark <122068506+radarroark@users.noreply.github.com> Date: Sat, 10 Jan 2026 16:07:11 -0500 Subject: [PATCH 02/11] make any other List impls into a xitdb ArrayList --- src/xitdb/util/conversion.clj | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/xitdb/util/conversion.clj b/src/xitdb/util/conversion.clj index 22c10b6..a8911ce 100644 --- a/src/xitdb/util/conversion.clj +++ b/src/xitdb/util/conversion.clj @@ -190,7 +190,9 @@ (.write cursor nil) (.slot (list->LinkedArrayListCursor! cursor v))) - (validation/vector-or-chunked? v) + (or (validation/vector-or-chunked? v) + ;; any other List implementations should just be an ArrayList + (instance? java.util.List v)) (do (.write cursor nil) (.slot (coll->ArrayListCursor! cursor v))) From 0bea3777992862b16a2e1c99f3bf8f53441a6f3e Mon Sep 17 00:00:00 2001 From: radar roark <122068506+radarroark@users.noreply.github.com> Date: Sat, 10 Jan 2026 16:07:33 -0500 Subject: [PATCH 03/11] bump xitdb --- deps.edn | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps.edn b/deps.edn index a7a70d3..5adbbf6 100644 --- a/deps.edn +++ b/deps.edn @@ -1,6 +1,6 @@ {:paths ["src" "test"] :deps {org.clojure/clojure {:mvn/version "1.12.0"} - io.github.radarroark/xitdb {:mvn/version "0.20.0"}} + io.github.radarroark/xitdb {:mvn/version "0.28.0"}} :aliases {:test {:extra-deps {io.github.cognitect-labs/test-runner From f1e988c6cb53c3766ab2e7963f81bf47146b5dbc Mon Sep 17 00:00:00 2001 From: radar roark <122068506+radarroark@users.noreply.github.com> Date: Sat, 10 Jan 2026 17:10:32 -0500 Subject: [PATCH 04/11] add deref-at --- src/xitdb/db.clj | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/xitdb/db.clj b/src/xitdb/db.clj index 4a1e37f..6577df5 100644 --- a/src/xitdb/db.clj +++ b/src/xitdb/db.clj @@ -122,6 +122,13 @@ [xdb] (.count (read-history (-> xdb .tldbro .get)))) +(defn deref-at + "Returns the version of the data at the specified index." + [xdb index] + (let [history (read-history (-> xdb .tldbro .get)) + cursor (.getCursor history index)] + (xtypes/read-from-cursor cursor false))) + (deftype XITDBDatabase [tldbro rwdb lock] java.io.Closeable @@ -131,9 +138,7 @@ clojure.lang.IDeref (deref [this] - (let [history (read-history (.get tldbro)) - cursor (.getCursor history -1)] - (xtypes/read-from-cursor cursor false))) + (deref-at this -1)) clojure.lang.IAtom From 6972e80b90464fb76c5b49a65919b43753d3b11a Mon Sep 17 00:00:00 2001 From: radar roark <122068506+radarroark@users.noreply.github.com> Date: Sat, 10 Jan 2026 19:26:10 -0500 Subject: [PATCH 05/11] make read-only types return in-memory data for write operations --- src/xitdb/array_list.clj | 10 +++++----- src/xitdb/hash_map.clj | 14 +++++++------- src/xitdb/hash_set.clj | 12 ++++++------ src/xitdb/linked_list.clj | 8 ++++---- 4 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/xitdb/array_list.clj b/src/xitdb/array_list.clj index 82b18d6..5c1c733 100644 --- a/src/xitdb/array_list.clj +++ b/src/xitdb/array_list.clj @@ -18,11 +18,11 @@ (count [_] (.count ral)) - (cons [_ o] - (throw (UnsupportedOperationException. "XITDBArrayList is read-only"))) + (cons [this o] + (cons o (common/materialize this))) - (empty [_] - (throw (UnsupportedOperationException. "XITDBArrayList is read-only"))) + (empty [this] + []) (equiv [this other] (and (sequential? other) @@ -33,7 +33,7 @@ clojure.lang.IPersistentVector (assocN [this i val] - (throw (UnsupportedOperationException. "XITDBArrayList is read-only"))) + (assoc (common/materialize this) i val)) (length [this] (.count ral)) diff --git a/src/xitdb/hash_map.clj b/src/xitdb/hash_map.clj index 8be3add..331a9f7 100644 --- a/src/xitdb/hash_map.clj +++ b/src/xitdb/hash_map.clj @@ -35,21 +35,21 @@ (clojure.lang.MapEntry. key v)))) (assoc [this k v] - (throw (UnsupportedOperationException. "XITDBHashMap is read-only"))) + (assoc (common/materialize this) k v)) clojure.lang.IPersistentMap - (without [_ _] - (throw (UnsupportedOperationException. "XITDBHashMap is read-only"))) + (without [this k] + (dissoc (common/materialize this) k)) (count [this] (operations/map-item-count rhm)) clojure.lang.IPersistentCollection - (cons [_ _] - (throw (UnsupportedOperationException. "XITDBHashMap is read-only"))) + (cons [this o] + (cons o (common/materialize this))) - (empty [_] - (throw (UnsupportedOperationException. "XITDBHashMap is read-only"))) + (empty [this] + {}) (equiv [this other] (and (instance? clojure.lang.IPersistentMap other) diff --git a/src/xitdb/hash_set.clj b/src/xitdb/hash_set.clj index ff1110d..5db27d7 100644 --- a/src/xitdb/hash_set.clj +++ b/src/xitdb/hash_set.clj @@ -15,8 +15,8 @@ (deftype XITDBHashSet [^ReadHashSet rhs] clojure.lang.IPersistentSet - (disjoin [_ k] - (throw (UnsupportedOperationException. "XITDBHashSet is read-only"))) + (disjoin [this k] + (disj (common/materialize this) k)) (contains [this k] (operations/set-contains? rhs k)) @@ -26,11 +26,11 @@ k)) clojure.lang.IPersistentCollection - (cons [_ o] - (throw (UnsupportedOperationException. "XITDBHashSet is read-only"))) + (cons [this o] + (cons o (common/materialize this))) - (empty [_] - (throw (UnsupportedOperationException. "XITDBHashSet is read-only"))) + (empty [this] + #{}) (equiv [this other] (and (instance? clojure.lang.IPersistentSet other) diff --git a/src/xitdb/linked_list.clj b/src/xitdb/linked_list.clj index c9b16a4..28adf98 100644 --- a/src/xitdb/linked_list.clj +++ b/src/xitdb/linked_list.clj @@ -19,11 +19,11 @@ (count [_] (.count rlal)) - (cons [_ o] - (throw (UnsupportedOperationException. "XITDBLinkedArrayList is read-only"))) + (cons [this o] + (cons o (common/materialize this))) - (empty [_] - (throw (UnsupportedOperationException. "XITDBLinkedArrayList is read-only"))) + (empty [this] + '()) (equiv [this other] (and (sequential? other) From 5b517b53e71f0df122ac9ae641ce5b326aa06d8c Mon Sep 17 00:00:00 2001 From: radar roark <122068506+radarroark@users.noreply.github.com> Date: Sat, 10 Jan 2026 21:38:51 -0500 Subject: [PATCH 06/11] update readme --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index eab8251..c39a9dd 100644 --- a/README.md +++ b/README.md @@ -135,6 +135,14 @@ from the respective `history index`. The root data structure of a xitdb database is a ArrayList, called 'history'. Each transaction adds a new entry into this array, which points to the latest value of the database (usually a map). + +```clojure +(xdb/deref-at db -1) ;; the most recent value, same as @db +(xdb/deref-at db -2) ;; the second most recent value +(xdb/deref-at db 0) ;; the earliest value +(xdb/deref-at db 1) ;; the second value +``` + It is also possible to create a transaction which returns the previous and current values of the database, by setting the `*return-history?*` binding to `true`. From ba16b8f10354e5b632278632d8bbbd428953a077 Mon Sep 17 00:00:00 2001 From: radar roark <122068506+radarroark@users.noreply.github.com> Date: Sat, 10 Jan 2026 21:51:53 -0500 Subject: [PATCH 07/11] store nils as Tag/NONE --- src/xitdb/util/conversion.clj | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/xitdb/util/conversion.clj b/src/xitdb/util/conversion.clj index a8911ce..91e445d 100644 --- a/src/xitdb/util/conversion.clj +++ b/src/xitdb/util/conversion.clj @@ -42,7 +42,6 @@ {:keyword "kw" :boolean "bl" :key-integer "ki" - :nil "nl" ;; TODO: Could use Tag/NONE instead :inst "in" :date "da" :coll "co" @@ -120,7 +119,7 @@ (Database$Float. v) (nil? v) - (database-bytes "" (fmt-tag-value :nil)) + nil (instance? java.time.Instant v) (database-bytes (str v) (fmt-tag-value :inst)) @@ -313,9 +312,6 @@ (java.util.Date/from (java.time.Instant/parse str)) - (= fmt-tag (fmt-tag-value :nil)) - nil - :else str))) From 230e5818896175ac2750c915b63707bcadc58903 Mon Sep 17 00:00:00 2001 From: radar roark <122068506+radarroark@users.noreply.github.com> Date: Sat, 10 Jan 2026 22:02:22 -0500 Subject: [PATCH 08/11] link to datascript gist --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c39a9dd..15b8708 100644 --- a/README.md +++ b/README.md @@ -111,7 +111,7 @@ Use `materialize` to convert a nested `XITDB` data structure to a native Clojure ## No query language Use `filter`, `group-by`, `reduce`, etc. -If you want a query engine, `datascript` works out of the box, you can store the datoms as a vector in the db. +If you want a query engine, [`datascript` works out of the box](https://gist.github.com/radarroark/663116fcd204f3f89a7e43f52fa676ef), you can store the datoms as a vector in the db. Here's a taste of how your queries could look like: ```clojure From 7b1c8929a5c51ee6107a2d6f15004cfbceb91757 Mon Sep 17 00:00:00 2001 From: radar roark <122068506+radarroark@users.noreply.github.com> Date: Sun, 11 Jan 2026 04:19:21 -0500 Subject: [PATCH 09/11] add -slot impls for read-only types --- src/xitdb/array_list.clj | 4 ++++ src/xitdb/hash_map.clj | 4 ++++ src/xitdb/hash_set.clj | 4 ++++ src/xitdb/linked_list.clj | 8 ++++++++ src/xitdb/util/conversion.clj | 6 +++--- 5 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/xitdb/array_list.clj b/src/xitdb/array_list.clj index 5c1c733..28bf5ed 100644 --- a/src/xitdb/array_list.clj +++ b/src/xitdb/array_list.clj @@ -106,6 +106,10 @@ (aset result len nil)) result)) + common/ISlot + (-slot [this] + (-> ral .cursor .slot)) + common/IUnwrap (-unwrap [this] ral) diff --git a/src/xitdb/hash_map.clj b/src/xitdb/hash_map.clj index 331a9f7..38e8a29 100644 --- a/src/xitdb/hash_map.clj +++ b/src/xitdb/hash_map.clj @@ -81,6 +81,10 @@ (kv-reduce [this f init] (operations/map-kv-reduce rhm #(common/-read-from-cursor %) f init)) + common/ISlot + (-slot [this] + (-> rhm .cursor .slot)) + common/IUnwrap (-unwrap [this] rhm) diff --git a/src/xitdb/hash_set.clj b/src/xitdb/hash_set.clj index 5db27d7..d79cd36 100644 --- a/src/xitdb/hash_set.clj +++ b/src/xitdb/hash_set.clj @@ -68,6 +68,10 @@ (remove [_] (throw (UnsupportedOperationException. "XITDBHashSet iterator is read-only")))))) + common/ISlot + (-slot [this] + (-> rhs .cursor .slot)) + common/IUnwrap (-unwrap [_] rhs) diff --git a/src/xitdb/linked_list.clj b/src/xitdb/linked_list.clj index 28adf98..b4c9acb 100644 --- a/src/xitdb/linked_list.clj +++ b/src/xitdb/linked_list.clj @@ -86,6 +86,14 @@ (aset result len nil)) result)) + common/ISlot + (-slot [this] + (-> rlal .cursor .slot)) + + common/IUnwrap + (-unwrap [_] + rlal) + Object (toString [this] (pr-str (into [] this)))) diff --git a/src/xitdb/util/conversion.clj b/src/xitdb/util/conversion.clj index 91e445d..077f9db 100644 --- a/src/xitdb/util/conversion.clj +++ b/src/xitdb/util/conversion.clj @@ -103,6 +103,9 @@ [v] (cond + (or (nil? v) (instance? Slot v)) + v + (string? v) (database-bytes v) @@ -118,9 +121,6 @@ (double? v) (Database$Float. v) - (nil? v) - nil - (instance? java.time.Instant v) (database-bytes (str v) (fmt-tag-value :inst)) From 460717b075a3ddba4e3c520f128c5b4b47d85ba4 Mon Sep 17 00:00:00 2001 From: radar roark <122068506+radarroark@users.noreply.github.com> Date: Sun, 11 Jan 2026 05:47:34 -0500 Subject: [PATCH 10/11] add materialize-shallow --- src/xitdb/array_list.clj | 10 ++++++++-- src/xitdb/common.clj | 3 +++ src/xitdb/hash_map.clj | 12 +++++++++--- src/xitdb/hash_set.clj | 9 +++++++-- src/xitdb/linked_list.clj | 8 +++++++- src/xitdb/util/conversion.clj | 18 +++++++++++++----- 6 files changed, 47 insertions(+), 13 deletions(-) diff --git a/src/xitdb/array_list.clj b/src/xitdb/array_list.clj index 28bf5ed..9f00dc6 100644 --- a/src/xitdb/array_list.clj +++ b/src/xitdb/array_list.clj @@ -19,7 +19,7 @@ (.count ral)) (cons [this o] - (cons o (common/materialize this))) + (cons o (common/-materialize-shallow this))) (empty [this] []) @@ -33,7 +33,7 @@ clojure.lang.IPersistentVector (assocN [this i val] - (assoc (common/materialize this) i val)) + (assoc (common/-materialize-shallow this) i val)) (length [this] (.count ral)) @@ -128,6 +128,12 @@ (reduce (fn [a v] (conj a (common/materialize v))) [] (seq this)))) +(extend-protocol common/IMaterializeShallow + XITDBArrayList + (-materialize-shallow [this] + (reduce (fn [a v] + (conj a v)) [] (seq this)))) + ;;----------------------------------------------- (deftype XITDBWriteArrayList [^WriteArrayList wal] diff --git a/src/xitdb/common.clj b/src/xitdb/common.clj index 59bce43..e94bace 100644 --- a/src/xitdb/common.clj +++ b/src/xitdb/common.clj @@ -9,6 +9,9 @@ (defprotocol IMaterialize (-materialize [this])) +(defprotocol IMaterializeShallow + (-materialize-shallow [this])) + (defprotocol IUnwrap (-unwrap [this])) diff --git a/src/xitdb/hash_map.clj b/src/xitdb/hash_map.clj index 38e8a29..e564285 100644 --- a/src/xitdb/hash_map.clj +++ b/src/xitdb/hash_map.clj @@ -35,18 +35,18 @@ (clojure.lang.MapEntry. key v)))) (assoc [this k v] - (assoc (common/materialize this) k v)) + (assoc (common/-materialize-shallow this) k v)) clojure.lang.IPersistentMap (without [this k] - (dissoc (common/materialize this) k)) + (dissoc (common/-materialize-shallow this) k)) (count [this] (operations/map-item-count rhm)) clojure.lang.IPersistentCollection (cons [this o] - (cons o (common/materialize this))) + (. clojure.lang.RT (conj (common/-materialize-shallow this) o))) (empty [this] {}) @@ -103,6 +103,12 @@ (reduce (fn [m [k v]] (assoc m k (common/materialize v))) {} (seq this)))) +(extend-protocol common/IMaterializeShallow + XITDBHashMap + (-materialize-shallow [this] + (reduce (fn [m [k v]] + (assoc m k v)) {} (seq this)))) + ;--------------------------------------------------- diff --git a/src/xitdb/hash_set.clj b/src/xitdb/hash_set.clj index d79cd36..ded6dd1 100644 --- a/src/xitdb/hash_set.clj +++ b/src/xitdb/hash_set.clj @@ -16,7 +16,7 @@ (deftype XITDBHashSet [^ReadHashSet rhs] clojure.lang.IPersistentSet (disjoin [this k] - (disj (common/materialize this) k)) + (disj (common/-materialize-shallow this) k)) (contains [this k] (operations/set-contains? rhs k)) @@ -27,7 +27,7 @@ clojure.lang.IPersistentCollection (cons [this o] - (cons o (common/materialize this))) + (cons o (common/-materialize-shallow this))) (empty [this] #{}) @@ -89,6 +89,11 @@ (-materialize [this] (into #{} (map common/materialize (seq this))))) +(extend-protocol common/IMaterializeShallow + XITDBHashSet + (-materialize-shallow [this] + (into #{} (seq this)))) + ;; Writable version of the set (deftype XITDBWriteHashSet [^WriteHashSet whs] clojure.lang.IPersistentSet diff --git a/src/xitdb/linked_list.clj b/src/xitdb/linked_list.clj index b4c9acb..c3120a4 100644 --- a/src/xitdb/linked_list.clj +++ b/src/xitdb/linked_list.clj @@ -20,7 +20,7 @@ (.count rlal)) (cons [this o] - (cons o (common/materialize this))) + (cons o (common/-materialize-shallow this))) (empty [this] '()) @@ -108,6 +108,12 @@ (reduce (fn [a v] (conj a (common/materialize v))) [] (seq this)))) +(extend-protocol common/IMaterializeShallow + XITDBLinkedArrayList + (-materialize-shallow [this] + (reduce (fn [a v] + (conj a v)) [] (seq this)))) + ;; ----------------------------------------------------------------- (deftype XITDBWriteLinkedArrayList [^WriteLinkedArrayList wlal] diff --git a/src/xitdb/util/conversion.clj b/src/xitdb/util/conversion.clj index 077f9db..36b54d9 100644 --- a/src/xitdb/util/conversion.clj +++ b/src/xitdb/util/conversion.clj @@ -189,6 +189,11 @@ (.write cursor nil) (.slot (list->LinkedArrayListCursor! cursor v))) + (set? v) + (do + (.write cursor nil) + (.slot (set->WriteCursor! cursor v))) + (or (validation/vector-or-chunked? v) ;; any other List implementations should just be an ArrayList (instance? java.util.List v)) @@ -196,11 +201,6 @@ (.write cursor nil) (.slot (coll->ArrayListCursor! cursor v))) - (set? v) - (do - (.write cursor nil) - (.slot (set->WriteCursor! cursor v))) - :else (primitive-for v))) @@ -223,6 +223,10 @@ (let [v-cursor (.appendCursor write-array)] (list->LinkedArrayListCursor! v-cursor v)) + (set? v) + (let [v-cursor (.appendCursor write-array)] + (set->WriteCursor! v-cursor v)) + (validation/vector-or-chunked? v) (let [v-cursor (.appendCursor write-array)] (coll->ArrayListCursor! v-cursor v)) @@ -251,6 +255,10 @@ (let [v-cursor (.appendCursor write-list)] (list->LinkedArrayListCursor! v-cursor v)) + (set? v) + (let [v-cursor (.appendCursor write-list)] + (set->WriteCursor! v-cursor v)) + (validation/vector-or-chunked? v) (let [v-cursor (.appendCursor write-list)] (coll->ArrayListCursor! v-cursor v)) From 93b508b8adf21342fe5719230fb93d7ee2f00ce2 Mon Sep 17 00:00:00 2001 From: radar roark <122068506+radarroark@users.noreply.github.com> Date: Sun, 11 Jan 2026 06:38:41 -0500 Subject: [PATCH 11/11] make reset! use the slot if available --- src/xitdb/db.clj | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/xitdb/db.clj b/src/xitdb/db.clj index 6577df5..92332a7 100644 --- a/src/xitdb/db.clj +++ b/src/xitdb/db.clj @@ -51,7 +51,9 @@ Returns new history index." [^WriteArrayList history new-value] (append-context! history nil (fn [^WriteCursor cursor] - (conversion/v->slot! cursor new-value)))) + (if (satisfies? common/ISlot new-value) + (.write cursor (common/-slot new-value)) + (conversion/v->slot! cursor new-value))))) (defn v->slot! "Converts a value to a slot which can be written to a cursor.