MST.Tree
(mst v0.1.0)
View Source
An in-memory Merkle Search Tree.
MST.Tree is the primary interface for building and querying MSTs. It pairs
a root CID (or nil for an empty tree) with a block store that maps CIDs to
decoded MST.Node structs.
All mutation operations (put/3, delete/3) return a new MST.Tree —
the data structure is persistent/immutable. The underlying store accumulates
all written nodes across mutations; stale nodes are not removed (no GC).
Example
store = MST.Store.Memory.new()
tree = MST.Tree.new(store)
val = DASL.CID.compute("my record data")
{:ok, tree} = MST.Tree.put(tree, "collection/record-key", val)
{:ok, ^val} = MST.Tree.get(tree, "collection/record-key")
{:ok, tree} = MST.Tree.delete(tree, "collection/record-key")
{:error, :not_found} = MST.Tree.get(tree, "collection/record-key")
Summary
Functions
Collects all MST nodes reachable from the root into a map of CID → encoded bytes.
Removes key from the tree.
Returns a tree referencing an existing root node CID in the given store.
Looks up key in the tree.
Returns the number of key-value pairs in the tree.
Returns a new, empty tree backed by the given store.
Inserts or updates key → value in the tree.
Returns a lazy stream of {key, value_cid} pairs in sorted order.
Returns all key-value pairs in the tree as a sorted list.
Types
@type store() :: MST.Store.t()
@type t() :: %MST.Tree{root: DASL.CID.t() | nil, store: store()}
@type tree_error() :: {:error, atom()}
Functions
@spec collect_blocks(t()) :: {:ok, %{required(DASL.CID.t()) => binary()}} | tree_error()
Collects all MST nodes reachable from the root into a map of CID → encoded bytes.
Useful for serialising the tree to a CAR file.
Examples
iex> store = MST.Store.Memory.new()
iex> tree = MST.Tree.new(store)
iex> val = DASL.CID.compute("x")
iex> {:ok, tree} = MST.Tree.put(tree, "col/a", val)
iex> {:ok, blocks} = MST.Tree.collect_blocks(tree)
iex> map_size(blocks) >= 1
true
@spec delete(t(), binary()) :: {:ok, t()} | {:error, :not_found} | tree_error()
Removes key from the tree.
Returns {:ok, new_tree} on success, {:error, :not_found} if the key
does not exist.
Examples
iex> store = MST.Store.Memory.new()
iex> tree = MST.Tree.new(store)
iex> val = DASL.CID.compute("data")
iex> {:ok, tree2} = MST.Tree.put(tree, "col/key", val)
iex> {:ok, tree3} = MST.Tree.delete(tree2, "col/key")
iex> MST.Tree.get(tree3, "col/key")
{:error, :not_found}
@spec from_root(DASL.CID.t() | nil, store()) :: t()
Returns a tree referencing an existing root node CID in the given store.
Use this to wrap an already-populated store (e.g. after loading from a CAR file).
Examples
iex> store = MST.Store.Memory.new()
iex> node = MST.Node.empty()
iex> {:ok, cid} = MST.Node.cid(node)
iex> store = MST.Store.put(store, cid, node)
iex> tree = MST.Tree.from_root(cid, store)
iex> tree.root == cid
true
@spec get(t(), binary()) :: {:ok, DASL.CID.t()} | {:error, :not_found} | tree_error()
Looks up key in the tree.
Returns {:ok, value_cid} if found, {:error, :not_found} otherwise.
Examples
iex> store = MST.Store.Memory.new()
iex> tree = MST.Tree.new(store)
iex> MST.Tree.get(tree, "col/key")
{:error, :not_found}
@spec length(t()) :: {:ok, non_neg_integer()} | tree_error()
Returns the number of key-value pairs in the tree.
Examples
iex> store = MST.Store.Memory.new()
iex> tree = MST.Tree.new(store)
iex> {:ok, 0} = MST.Tree.length(tree)
iex> val = DASL.CID.compute("x")
iex> {:ok, tree} = MST.Tree.put(tree, "col/a", val)
iex> MST.Tree.length(tree)
{:ok, 1}
Returns a new, empty tree backed by the given store.
Examples
iex> tree = MST.Tree.new(MST.Store.Memory.new())
iex> tree.root
nil
@spec put(t(), binary(), DASL.CID.t()) :: {:ok, t()} | tree_error()
Inserts or updates key → value in the tree.
Returns {:ok, new_tree} on success. The new tree shares the store with the
old tree, but both may be used independently — nodes are append-only.
Examples
iex> store = MST.Store.Memory.new()
iex> tree = MST.Tree.new(store)
iex> val = DASL.CID.compute("data")
iex> {:ok, tree2} = MST.Tree.put(tree, "col/key", val)
iex> MST.Tree.get(tree2, "col/key")
{:ok, val}
@spec stream(t()) :: Enumerable.t()
Returns a lazy stream of {key, value_cid} pairs in sorted order.
The stream reads nodes from the store on demand. Raises on missing nodes (consistent with lazy stream semantics).
Examples
iex> store = MST.Store.Memory.new()
iex> tree = MST.Tree.new(store)
iex> val = DASL.CID.compute("x")
iex> {:ok, tree} = MST.Tree.put(tree, "col/a", val)
iex> tree |> MST.Tree.stream() |> Enum.to_list()
[{"col/a", val}]
@spec to_list(t()) :: {:ok, [{binary(), DASL.CID.t()}]} | tree_error()
Returns all key-value pairs in the tree as a sorted list.
Examples
iex> store = MST.Store.Memory.new()
iex> tree = MST.Tree.new(store)
iex> val = DASL.CID.compute("data")
iex> {:ok, tree} = MST.Tree.put(tree, "col/b", val)
iex> {:ok, tree} = MST.Tree.put(tree, "col/a", val)
iex> {:ok, pairs} = MST.Tree.to_list(tree)
iex> Enum.map(pairs, &elem(&1, 0))
["col/a", "col/b"]