object's descendants and children enhancements
marshall-lee opened this issue · 0 comments
Currently, object.descendants
is actualy a call to Model.descendants_of(object.path, min_depth: 1)
. min_depth: 1
here is used because both @>
operator and "#{path}.*"
lquery pattern also match rows with paths that are equal to object.path
. But we don't want to see the object
among results of querying descendants
and children
. So we use "#{path}.{1,}"
and "#{path},{1}"
lquery patterns for descendants
and children
respectively. It works well but it's not exactly what should happen and now i'll tell why.
In 'classic' definition of a tree it's a set of nodes each having a link to parent node. Sometimes we call a node with an empty parent link (equal to nil
) as a root (but often a root is a something virtual that doesn't exist because unless we have many such records with parent == nil
and it's not a tree but forest). From ltree
point of view we consider path
field value as a root-to-leaf path and we call an object with path a.b.c
a parent of objects with paths a.b.c.d
and a.b.c.e
.
But the problem is that we don't require path
field to be unique. There could be two or more records with path equal to a.b.c
. Which one is a parent of a.b.c.d
? Yeah, one could add the UNIQUE
index on that field and define parent
method according to the rule described above but other one may not want this unique behaviour.
We must not use the assumption that path
field is unique. This is why we'll not implement parent
method in acts_as_ltree
(but we'll give a recommendation in docs/tutorials how to do it). And this is why we must reimplement children
and descendants
methods.
So my proposition is that following things should be done:
- Rename
#descendants
method to#self_and_descendants
and reimplement it so it'll be possible to pass the same set of options as todescendants_of
class method. No moremin_depth: 1
! - Implement new
#descendants
by calling#self_and_descendants
with addingwhere(self.class.arel_table[self.class.primary_key].not_eq(self.id))
condition to the resulting relation. (ifobject.id = 123
thenWHERE id != 123
will be added to the sql string) - Reimplement
children
to be simply a call to#self_and_descendants
withexact_depth: 1
. - Implement
#strict_descendants
as a shortcut ofself_and_descendants min_depth: 1
to save old behaviour because it's good too for some cases.