                     {:data/type (:data/type node)}))))
 
 
-; TODO: read-range
-; narrow children by which could contain the range, recurse in order
+(defn read-range
+  "Read a lazy sequence of key/map tuples which contain the field data for the
+  records whose keys lie in the given range, inclusive. A nil boundary includes
+  all records in that range direction."
+  [store node fields start-key end-key]
+  (case (:data/type node)
+    :merkle-db/index-node
+    (->
+      (concat (map vector (::children node) (::keys node))
+              [[(last (::children node))]])
+      (->>
+        (partition 2 1)
+        (map (fn [[[_ start-key] [link end-key]]]
+               [link [start-key end-key]]))
+        (cons [(first (::children node)) nil (first (::key node))]))
+      (cond->>
+        start-key
+          (drop-while #(key/before? (nth % 2) start-key))
+        end-key
+          (take-while #(key/after? end-key (nth % 1))))
+      ,,,
+      #_
+      (read-tablets store part fields tablet/read-range start-key end-key))
+
+    :merkle-db/partition
+    (part/read-range store node fields start-key end-key)
+
+    (throw (ex-info (str "Unsupported data-tree node type: "
+                         (pr-str (:data/type node)))
+                    {:data/type (:data/type node)}))))
 
 
 ; TODO: read-slice


;; ## Read Functions

(defn read-all
  "Read a lazy sequence of key/map tuples which contain the requested field data
  for every record in the subtree. This function works on both index nodes and

    (throw (ex-info (str "Unsupported data-tree node type: "
                         (pr-str (:data/type node)))
                    {:data/type (:data/type node)}))))


(defn read-batch
