Skip to content

Commit 1e94ee2

Browse files
committed
Support extra protocols required for editscript diff-ing
1 parent 5cc0222 commit 1e94ee2

File tree

4 files changed

+121
-0
lines changed

4 files changed

+121
-0
lines changed

src/xitdb/array_list.clj

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,10 +76,21 @@
7676
2 (.invoke this (first args) (second args))
7777
(throw (IllegalArgumentException. "Wrong number of args passed to XITDBArrayList"))))
7878

79+
clojure.lang.IReduce
80+
(reduce [this f]
81+
(let [s (seq this)]
82+
(if s
83+
(reduce f (first s) (rest s))
84+
(f))))
85+
7986
clojure.lang.IReduceInit
8087
(reduce [this f init]
8188
(reduce f init (array-seq ral)))
8289

90+
clojure.core.protocols/IKVReduce
91+
(kv-reduce [this f init]
92+
(util/array-kv-reduce ral #(common/-read-from-cursor %) f init))
93+
8394
java.util.Collection
8495
(^objects toArray [this]
8596
(to-array (into [] this)))
@@ -185,6 +196,10 @@
185196
(seq [this]
186197
(array-seq wal))
187198

199+
clojure.core.protocols/IKVReduce
200+
(kv-reduce [this f init]
201+
(util/array-kv-reduce wal #(common/-read-from-cursor %) f init))
202+
188203
clojure.lang.IObj
189204
(withMeta [this _]
190205
this)

src/xitdb/hash_map.clj

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
[xitdb.common :as common]
44
[xitdb.xitdb-util :as util])
55
(:import
6+
[clojure.core.protocols IKVReduce]
67
(io.github.radarroark.xitdb ReadCursor ReadHashMap WriteCursor WriteHashMap)))
78

89
(defn map-seq
@@ -11,6 +12,7 @@
1112
(util/map-seq rhm #(common/-read-from-cursor %)))
1213

1314
(deftype XITDBHashMap [^ReadHashMap rhm]
15+
1416
clojure.lang.ILookup
1517
(valAt [this key]
1618
(.valAt this key nil))
@@ -73,6 +75,9 @@
7375
(remove [_]
7476
(throw (UnsupportedOperationException. "XITDBHashMap iterator is read-only"))))))
7577

78+
clojure.core.protocols/IKVReduce
79+
(kv-reduce [this f init]
80+
(util/map-kv-reduce rhm #(common/-read-from-cursor %) f init))
7681

7782
common/IUnwrap
7883
(-unwrap [this]
@@ -157,6 +162,10 @@
157162
(seq [this]
158163
(map-seq whm))
159164

165+
clojure.core.protocols/IKVReduce
166+
(kv-reduce [this f init]
167+
(util/map-kv-reduce whm #(common/-read-from-cursor %) f init))
168+
160169
common/ISlot
161170
(-slot [this]
162171
(-> whm .cursor .slot))

src/xitdb/xitdb_util.clj

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -467,4 +467,37 @@
467467
(lazy-seq (cons value (lazy-iter))))))]
468468
(lazy-iter)))
469469

470+
(defn map-kv-reduce
471+
"Efficiently reduces over key-value pairs in a ReadHashMap, skipping hidden keys."
472+
[^ReadHashMap rhm read-from-cursor f init]
473+
(let [it (.iterator rhm)]
474+
(loop [result init]
475+
(if (.hasNext it)
476+
(let [cursor (.next it)
477+
kv (.readKeyValuePair cursor)
478+
k (read-bytes-with-format-tag (.-keyCursor kv))]
479+
(if (contains? hidden-keys k)
480+
(recur result)
481+
(let [v (read-from-cursor (.-valueCursor kv))
482+
new-result (f result k v)]
483+
(if (reduced? new-result)
484+
@new-result
485+
(recur new-result)))))
486+
result))))
487+
488+
(defn array-kv-reduce
489+
"Efficiently reduces over index-value pairs in a ReadArrayList."
490+
[^ReadArrayList ral read-from-cursor f init]
491+
(let [count (.count ral)]
492+
(loop [i 0
493+
result init]
494+
(if (< i count)
495+
(let [cursor (.getCursor ral i)
496+
v (read-from-cursor cursor)
497+
new-result (f result i v)]
498+
(if (reduced? new-result)
499+
@new-result
500+
(recur (inc i) new-result)))
501+
result))))
502+
470503

test/xitdb/kv_reduce_test.clj

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
(ns xitdb.kv-reduce-test
2+
(:require
3+
[clojure.test :refer :all]
4+
[xitdb.test-utils :as tu :refer [with-db]]))
5+
6+
(deftest kv-reduce-test
7+
(with-db [db (tu/test-db)]
8+
(testing "IKVReduce implementation for XITDBHashMap"
9+
(reset! db {:a 1 :b 2 :c 3})
10+
11+
;; Test basic reduce operation
12+
(let [sum (reduce-kv (fn [acc k v] (+ acc v)) 0 @db)]
13+
(is (= 6 sum)))
14+
15+
;; Test early termination with reduced
16+
(let [first-key (reduce-kv (fn [acc k v] (reduced k)) nil @db)]
17+
(is (contains? #{:a :b :c} first-key)))
18+
19+
;; Test key-value accumulation
20+
(let [kvs (reduce-kv (fn [acc k v] (conj acc [k v])) [] @db)]
21+
(is (= 3 (count kvs)))
22+
(is (every? #(and (keyword? (first %)) (number? (second %))) kvs)))
23+
24+
;; Test with nested maps
25+
(reset! db {:outer {:inner {:value 42}}})
26+
(let [nested-value (reduce-kv (fn [acc k v]
27+
(if (= k :outer)
28+
(reduce-kv (fn [acc2 k2 v2]
29+
(if (= k2 :inner)
30+
(get v2 :value)
31+
acc2)) acc v)
32+
acc))
33+
nil @db)]
34+
(is (= 42 nested-value))))))
35+
36+
(deftest array-kv-reduce-test
37+
(with-db [db (tu/test-db)]
38+
(testing "IKVReduce implementation for XITDBArrayList"
39+
(reset! db [10 20 30 40])
40+
41+
;; Test basic reduce-kv operation (sum of indices * values)
42+
(let [weighted-sum (reduce-kv (fn [acc idx val] (+ acc (* idx val))) 0 @db)]
43+
(is (= 200 weighted-sum))) ; 0*10 + 1*20 + 2*30 + 3*40 = 0 + 20 + 60 + 120 = 200
44+
45+
;; Test early termination with reduced
46+
(let [first-val (reduce-kv (fn [acc idx val] (reduced val)) nil @db)]
47+
(is (= 10 first-val)))
48+
49+
;; Test index-value accumulation
50+
(let [idx-vals (reduce-kv (fn [acc idx val] (conj acc [idx val])) [] @db)]
51+
(is (= [[0 10] [1 20] [2 30] [3 40]] idx-vals)))
52+
53+
;; Test with nested vectors
54+
(reset! db [[1 2] [3 4] [5 6]])
55+
(let [sum-by-index (reduce-kv (fn [acc idx inner-vec]
56+
(+ acc (reduce-kv (fn [acc2 idx2 val2]
57+
(+ acc2 (* idx idx2 val2)))
58+
0 inner-vec)))
59+
0 @db)]
60+
;; idx=0: 0*0*1 + 0*1*2 = 0
61+
;; idx=1: 1*0*3 + 1*1*4 = 4
62+
;; idx=2: 2*0*5 + 2*1*6 = 12
63+
;; Total: 16
64+
(is (= 16 sum-by-index))))))

0 commit comments

Comments
 (0)