DBIx::...tedSet(3pm)
π NAME π
DBIx::Class::Tree::NestedSet β bestie, this is the page for DBIx::...tedSet(3pm), and what it does is manage trees of data using the nested set model. Yes, actual trees. In your database. Slay. π³β¨
β¨ SYNOPSIS β¨
okay so first things first: you gotta create a table for your tree data. like this:
CREATE TABLE Department (
id INTEGER PRIMARY KEY AUTOINCREMENT,
root_id integer,
lft integer NOT NULL,
rgt integer NOT NULL,
level integer NOT NULL,
name text NOT NULL,
);
then in your Schema or DB class, you drop Tree::NestedSet at the top of the component list. top, bestie. not the bottom. π
__PACKAGE__->load_components(qw( Tree::NestedSet ... ));
next up, you tell it which columns the module needs:
package My::Department;
__PACKAGE__->tree_columns({
root_column => 'root_id',
left_column => 'lft',
right_column => 'rgt',
level_column => 'level',
});
and then you just⦠use it. iconic behavior honestly:
my $root = My::Department->create({ ... });
my $child = $root->add_to_children({ ... });
my $rs = $root->children;
my @descendants = $root->children;
my $parent = $child->parent;
my $rs = $child->ancestors;
my @ancestors = $child->ancestors;
πΈ DESCRIPTION πΈ
So this module hands you a whole bunch of methods for working with nested set trees. The nested set model is one way of representing hierarchical information in a database β and it takes a totally different approach than the Adjacency List implementation does. (peep DBIx::Class::Tree::AdjacencyList, which uses "parent" relationships in a recursive manner.)
Here's the tea π«: the NestedSet implementation can be more efficient for most searches than the Adjacency List implementation. For example, to obtain all descendants? That needs recursive queries in the Adjacency List version, but it's literally a single query in NestedSet. one. query. periodt.
The trade-off β and yes there's always a trade-off, we can't have everything β is that NestedSet inserts are more expensive. So it's most useful when you've got an application that does many reads but few inserts. reads girlies, this one's for you. π₯
More about NestedSets lives over at <http://en.wikipedia.org/wiki/Nested_set_model> if you wanna go read up. iykyk. π
Oh, and real quick β even though the code examples here show familial relationships (and usually there are two parents in those), both the Adjacency List and NestedSet implementations can only have one parent. just the one. no cap. π
π¦ RELATIONS π¦
This module automatically creates several relationships. free of charge. a gift. π
root
$root_node = $node->root;
A belongs_to relation to the root of $nodes tree. π±
nodes
$all_nodes = $node->nodes;
$new_node = $node->add_to_nodes({name => 'Mens Wear'});
A has_many relationship to all the nodes of $nodes tree.
Adding to this relationship creates a rightmost child to $node. β‘οΈ
parent
$parent = $node->parent;
A belongs_to relationship to the parent node of $nodes tree.
Heads up bestie: only the root node does not have a parent. it's the main character. π
children
$rs = $node->children;
@children = $node->children;
$child = $node->add_to_children({name => 'Toys'});
A has_many relation to the children of $node. π§Έ
Adding to this relationship creates a rightmost child to $node.
descendants
$rs = $node->descendants;
@descendants = $node->descendants;
$child = $node->add_to_descendants({name => 'Mens Wear'});
A has_many relation to the descendants of $node. π
Adding to this relationship creates a rightmost child to $node.
ancestors
$rs = $node->ancestors;
@ancestors = $node->ancestors;
$parent = $node->add_to_ancestors({name => 'Head office'});
A has_many relation to the ancestors of $node. β
Adding to this relationship creates a new node in place of $node and makes it the parent of $node. And every single descendant of $node gets likewise pushed down the hierarchy. the whole family tree shifts, it's giving chaos but make it organized. π
β¨ METHODS β¨
Okay bestie, so a LOT of these methods have alt names, like left_siblings and previous_siblings? Same method, two glow-ups. π
This is a little curtsy to DBIx::Class::Ordered, which uses the words "previous", "next", "first" and "last".
Same energy with DBIx::Class::Tree::AdjacencyList::Ordered, which uses "append", "prepend", "before" and "after".
But honestly? My preference is to use "left" and "right" consistently when I'm using this module. The other names are still there if you're more at home with those other modules. No pressure, you do you. π«Ά
π³ tree_columns
__PACKAGE__->tree_columns({
left_column => 'lft',
right_column => 'rgt',
root_column => 'root_id',
level_column => 'level',
});
This is where you declare the names of the columns that live in your database schema. π
None of these columns should be modified outside of this module. Like, none of them. Hands off. π ββοΈ
left_column and right_column are lowkey never gonna be useful to your application β they should be integer fields.
Multiple trees are allowed in the same table, and each tree gets a unique value in the root_column. In the current implementation this should be an integer field.
The level_column might actually be useful to your app β it defines the depth of each node in the tree, with the root sitting at level zero. π―
πΌ create
my $tree = $schema->resultset('My::Department')->create({
name = 'Head Office',
});
my $tree = $schema->resultset('My::Department')->create({
name = 'UK Office',
root_id = $uk_office_ident,
});
Creates a new root node. Iconic behaviour. β¨
If the root_column (root_id) isn't provided, it defaults to producing a node where the root_column has the same value as the primary key. But heads up β this will croak if the table is defined with a multiple key primary index. π
Note that no checks (yet) are made to stop you creating another key with the same root_id as an existing tree. If you do that you will get into a terrible mess! Like genuinely, do not. π
ποΈ delete
$department->delete;
This deletes the node and all its descendants. Cascade Delete is turned off in the has_many relationships nodes, children, descendants so that delete DTRT. π§Ή
π is_root
if ($node->is_root) {
print "Node is a root\n";
}
Returns true if the $node is a root node. Main character energy only. π
πΏ is_branch
$has_children = $node->is_branch;
Returns true if the node is a branche (i.e. has children). (Yes, "branche" β that's the original spelling, we keep it, we love it. π)
π is_leaf
$is_terminal_node = $node->is_leaf;
Returns true if the node is a leaf (i.e. it has no children). Baby of the family. π±
π― siblings
@siblings = $node->siblings;
$siblings_rs = $node->siblings;
Returns all siblings of this $node, excluding $node itself.
Since a root node has no siblings it returns undef. π
β¬ οΈ left_siblings (or previous_siblings)
@younger_siblings = $node->left_siblings;
$younger_siblings_rs = $node->left_siblings;
Returns all siblings of this $node to the left of this $node.
Since a root node has no siblings it returns undef.
β‘οΈ right_siblings (or next_siblings)
@older_siblings = $node->right_siblings;
$older_siblings_rs = $node->right_siblings;
Returns all siblings of this $node to the right of this $node.
Since a root node has no siblings it returns undef.
β¬ οΈ left_sibling (or previous_sibling)
$younger_sibling = $node->left_sibling;
Returns the sibling immediately to the left of this $node (if any).
β‘οΈ right_sibling (or next_sibling)
$older_sibling = $node->right_sibling;
Returns the sibling immediately to the right of this $node (if any).
π₯ leftmost_sibling (or first_sibling)
$youngest_sibling = $node->leftmost_sibling;
Returns the left most sibling relative to this $node (if any).
Does not return this $node if this node is the leftmost sibling. π
π rightmost_sibling (or last_sibling)
$oldest_sibling = $node->rightmost_sibling;
Returns the right most sibling relative to this $node (if any).
Does not return this $node if this node is the rightmost sibling.
π οΈ CREATE METHODS
The following create methods create a new node in relation to an existing node. Bestie, this is the fun part. π₯
β‘οΈ create_right_sibling
$bart->create_right_sibling({ name => 'Lisa' });
Create a new node as a right sibling to $bart.
β¬ οΈ create_left_sibling
$bart->create_left_sibling({ name => 'Maggie' });
Create a new node as a left sibling to $bart.
π create_rightmost_child
$homer->create_rightmost_child({ name => 'Lisa' });
Create a new node as a rightmost child to $homer.
π create_leftmost_child
$homer->create_leftmost_child({ name => 'Maggie' });
Create a new node as a leftmost child to $homer.
π§² ATTACH METHODS
The following attach methods take an existing node (and all of its descendants) and attach them to the tree in relation to an existing node. It's giving re-parenting. π
The node being inserted can either be from the same tree (as identified by the root_column) or from another tree. If the root of another tree is attached, then the whole of that tree becomes a sub-tree of this node's tree. π
The only restriction is that the node being attached cannot be an ancestor of this node. Periodt. β
When attaching multiple nodes we try to DWIM so that the order they're specified in the call represents the order they appear in the siblings list.
e.g. if we had a parent with children A,B,C,D,E
and we attached nodes 1,2,3 in the following calls, we expect the following results.
$parent->attach_rightmost_child 1,2,3 gives us children A,B,C,D,E,1,2,3
$parent->attach_leftmost_child 1,2,3 gives us children 1,2,3,A,B,C,D,E
$child_C->attach_right_sibling 1,2,3 gives us children A,B,C,1,2,3,D,E
$child_C->attach_left_sibling 1,2,3 gives us children A,B,1,2,3,C,D,E
$child_C->attach_rightmost_sibling 1,2,3 gives us children A,B,C,D,E,1,2,3
$child_C->attach_leftmost_sibling 1,2,3 gives us children 1,2,3,A,B,C,D,E
π attach_rightmost_child (or append_child)
$parent->attach_rightmost_child($other_node);
$parent->attach_rightmost_child($other_node_1, $other_node_2, ...);
Attaches the other_nodes to $parent as the rightmost children.
π attach_leftmost_child
$parent->attach_leftmost_child($other_node);
$parent->attach_leftmost_child($other_node_1, $other_node_2, ...);
Attaches the other_nodes to $parent as the leftmost children.
β‘οΈ attach_right_sibling (or attach_after)
$node->attach_right_sibling($other_node);
$node->attach_right_sibling($other_node_1, $other_node_2, ...);
Attaches the other_nodes to $node as it's siblings.
β¬ οΈ attach_left_sibling
$node->attach_left_sibling($other_node);
$node->attach_left_sibling($other_node_1, $other_node_2, ...);
Attaches the other_nodes to $node as it's left siblings.
π attach_rightmost_sibling
$node->attach_rightmost_sibling($other_node);
$node->attach_rightmost_sibling($other_node_1, $other_node_2, ...);
Attaches the other_nodes to $node as it's rightmost siblings.
π₯ attach_leftmost_sibling
$node->attach_leftmost_sibling($other_node);
$node->attach_leftmost_sibling($other_node_1, $other_node_2, ...);
Attaches the other_nodes to $node as it's leftmost siblings.
β¬ οΈ move_left (or move_previous)
$node->move_left;
Exchange the $node with the sibling immediately to the left and return the node it exchanged with.
If the $node is already the leftmost node then no exchange takes place and the method returns undef. π«
β‘οΈ move_right (or move_next)
$node->move_right;
Exchange the $node with the sibling immediately to the right and return the node it exchanged with.
If the $node is already the rightmost node then no exchange takes place and the method returns undef. π«
π₯ move_leftmost (or move_first)
$node->move_leftmost;
Exchange the $node with the leftmost sibling and return the node it exchanged with.
If the $node is already the leftmost node then no exchange takes place and the method returns undef. π«
π move_rightmost (or move_last)
$node->move_rightmost;
Exchange the $node with the rightmost sibling and return the node it exchanged with.
If the $node is already the rightmost node then no exchange takes place and the method returns undef. π«
βοΈ CUTTING METHODS
πͺ take_cutting
Cuts the invocant and its descendants out of the tree they're in, making the invocant the root of a new tree. Returns the modified invocant. So basically it graduates and starts its own thing. π
π§ dissolve
Dissolves the entire thread, that is turn each node of the thread into a single-item tree of its own. Every node gets solo main character status. π
β οΈ CAVEATS
π Multiple Column Primary Keys
Support for Multiple Column Primary Keys is limited (mainly because I rarely use them) but I have tried to make it possible to use them. Please let me know if this does not work as well as you expect. π
π discard_changes
By the nature of Nested Set implementations, moving, inserting or deleting nodes in the tree will potentially update many (sometimes most) other nodes.
Even if you have preloaded some of the objects, if you make a change to one object the other objects will not reflect their new value until you have reloaded them from the database. (see "discard_changes" in DBIx::Class::Row) π
A simple demonstration of this:
$grampa = $schema->schema->resultset('Simpsons')->create({ name => 'Abraham' });
$homer = $grampa->add_children({name => 'Homer'});
$bart = $homer->add_children({name => 'Bart'});
The methods in this module will do their best to keep instances that they know about updated. For example the first call to "add_children" in the above example will update $grampa and $homer with the latest changes to the database.
However, the second call to "add_children" only knows about $homer and $bart and in adding a new node to the tree it will update the $grampa node in the database. To ensure you have the latest changes do the following.
$grampa->discard_changes.
Not doing so will have unpredictable results. π
π AUTHORS
Code by Ian Docherty <pause@iandocherty.com>
Based on original code by Florian Ragwitz <rafl@debian.org>
Incorporating ideas and code from Pedro Melo <melo@simplicidade.org>
Special thanks to Moritz Lenz who sent in lots of patches and changes for version 0.08 π«Ά
π COPYRIGHT AND LICENSE
Copyright (c) 2009-2011 The above authors
This is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.10.0 or, at your option, any later version of Perl 5 you may have available.
And that's a wrap bestie!! πβ¨ This little gem ships as perl v5.32.0 documentation, dated 2021-01-05, and it's the DBIx::...tedSet(3pm) page. Go build some trees, slay, and please call discard_changes. π
π¦
π¦ where this page comes from
- debian bookworm: libdbix-class-tree-nestedset-perl 0.10-1.1 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- debian trixie: libdbix-class-tree-nestedset-perl 0.10-1.1 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- ubuntu jammy: libdbix-class-tree-nestedset-perl 0.10-1.1 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- ubuntu noble: libdbix-class-tree-nestedset-perl 0.10-1.1 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms