View Source CTE (Closure Table v1.1.5)
The Closure Table for Elixir strategy, CTE for short, is a simple and elegant way of storing and working with hierarchies. It involves storing all paths through a tree, not just those with a direct parent-child relationship. You may want to chose this model, over the Nested Sets model, should you need referential integrity and to assign nodes to multiple trees.
With CTE you can navigate through hierarchies using a simple API, such as: finding the ascendants and descendants of a node, inserting and deleting nodes, moving entire sub-trees or print them as a digraph (.dot) file.
Quick example.
For this example we're using the in-Memory Adapter.
This Adapter
is useful for prototyping or with data structures
that can easily fit in memory; their persistence being taken care
of by other components. For more involved use cases, CTE integrates
with Ecto using a simple API.
When used from a module, the CTE expects the: :otp_app
and
:adapter
attributes, to be defined. The :otp_app
should point
to an OTP application that might provide additional configuration.
Equally so are the: :nodes
and the :paths
attributes. The :nodes
attribute, in the case of the Memory Adapter,
is a Map defining your nodes while the: :paths
attribute, is a list
containing the tree path between the nodes - a list of lists. For example:
defmodule CTM do
use CTE,
otp_app: :ct_empty,
adapter: CTE.Adapter.Memory,
nodes: %{
1 => %{id: 1, author: "Olie", comment: "Is Closure Table better than the Nested Sets?"},
2 => %{id: 2, author: "Rolie", comment: "It depends. Do you need referential integrity?"},
3 => %{id: 3, author: "Olie", comment: "Yeah."}
},
paths: [[1, 1, 0], [1, 2, 1], [1, 3, 2], [2, 2, 0], [2, 3, 1], [3, 3, 0]]
end
When using the CTE.Adapter.Ecto
, the: :nodes
attribute,
will be a Schema i.e. Post
, TreePath
, etc! In our initial
implementation, the nodes definitions must have at least the
:id
, as one of their properties. This caveat will be lifted
in a later implementation.
Add the CTM
module to your main supervision tree:
defmodule CTM.Application do
@moduledoc false
use Application
def start(_type, _args) do
opts = [strategy: :one_for_one, name: CTM.Supervisor]
Supervisor.start_link([CTM], opts)
end
end
Using iex -S mix
, for quickly experimenting with the CTE API:
find the descendants of comment #1
iex» CTM.descendants(1) {:ok, [3, 2]}
find the ancestors
iex» CTM.ancestors(2) {:ok, [1]} iex» CTM.ancestors(3) {:ok, [1]}
find the ancestors, with information about the node:
iex» CTM.ancestors(2, nodes: true) {:ok, [ %{ author: "Olie", comment: "Is Closure Table better than the Nested Sets?", id: 1 } ]}
move leafs/subtrees around. From being a child of comment #1, to becoming a child of comment #2, in the following example:
iex» CTM.move(3, 2) :ok iex» CTM.descendants(2) {:ok, [3]}
Please check the docs, the tests, and the examples folder, for more details.