as_nested_set

An ecto based Nested set model implementation for database

Installation

Add as_nested_set to your list of dependencies in mix.exs:

# use the stable version
def deps do
[{:as_nested_set, "~> 1.0", app: false}]
end
# use the latest version
def deps do
[{:as_nested_set, github: "https://github.com/secretworry/as_nested_set.git", app: false}]
end

Usage

To make use of as_nested_set your model has to have at least 4 fields: id, lft, rgt and parent_id. The name of those fields are configurable.

defmodule AsNestedSet.TestRepo.Migrations.MigrateAll do
use Ecto.Migration
def change do
create table(:taxons) do
add :name, :string
add :taxonomy_id, :id
add :parent_id, :id
add :lft, :integer
add :rgt, :integer
timestamps
end
end
end

Enable the nested set functionality by use AsNestedSet on your model

defmodule AsNestedSet.Taxon do
use AsNestedSet, repo: AsNestedSet.TestRepo, scope: [:taxonomy_id]
# ...
end

Options

You can config the name of required field through attributes:

defmodule AsNestedSet.Taxon do
@right_column :right
@left_column :left
@node_id_column :node_id
@parent_id_column :pid
# ...
end

You can also pass following to modify its behavior:

defmodule AsNestedSet.Taxon do
use AsNestedSet, repo: AsNestedSet.TestRepo, scope: [:taxonomy_id]
# ...
end

Model Operations

Once you have set up you model, you can then

Add a new node

target = Repo.find!(Taxon, 1)
# add to left
%Taxon{name: "left", taxonomy_id: 1} |> Taxon.create(target, :left)
# add to right
%Taxon{name: "right", taxonomy_id: 1} |> Taxon.create(target, :right)
# add as first child
%Taxon{name: "child", taxonomy_id: 1} |> Taxon.create(target, :child)
# add as parent
%Taxon{name: "parent", taxonomy_id: 1} |> Taxon.create(target, :parent)
# add as root
%Taxon{name: "root", taxonomy_id: 1} |> Taxon.create(:root)

Remove a specified node and all its descendants

target = Repo.find!(Taxon, 1)
Taxon.remove(target)

Query different nodes

# find all roots
Taxon.roots(%{taxonomy_id: 1})
# find all children of target
Taxon.children(target)
# find all the leaves for given scope
Taxon.leaves(%{taxonomy_id: 1})
# find all descendants
Taxon.descendants(target)
# include self
Taxon.self_and_descendants(target)
# find all ancestors
Taxon.ancestors(target)
#find all siblings (self included)
Taxon.self_and_siblings(target)