feincms / django-tree-queries

Adjacency-list trees for Django using recursive common table expressions. Supports PostgreSQL, sqlite, MySQL and MariaDB.
https://django-tree-queries.readthedocs.io/
BSD 3-Clause "New" or "Revised" License
433 stars 27 forks source link

Is it feasible to support get queryset descendants or ancestors like features in django-mptt #28

Open jukanntenn opened 2 years ago

jukanntenn commented 2 years ago

django-mptt has a feature get_queryset_descendants which query the descendants of tree nodes represented by a queryset. However, TreeQuerySet.descendants method in django-tree-queries only support query descendants of a certain tree node. After doing some experiments, I find a way to implement such feature, like bellow:

def descendants(self, of, include_self=False):
    ...
    extra_where = " or ".join(['instr(__tree.tree_path, "{sep}{pk}{sep}") <> 0'.format(
                        pk=self.model._meta.pk.get_db_prep_value(pk(obj), connection),
                        sep=SEPARATOR,
                    ) for obj in of])
            queryset = self.with_tree_fields().extra(
                # NOTE! The representation of tree_path is NOT part of the API.
                where=[
                    # XXX This *may* be unsafe with some primary key field types.
                    # It is certainly safe with integers.
                    extra_where
                ]
            )

The idea is if of parameter is a queryset rather than a single model instance, changing the where clause to or.

The downside of this approach is we need an extra database query. I also have no idea if there is performance issue for a large queryset.

Another idea I come up with is inject a a subquery in CTE, like bellow: change:

SELECT
            0 AS tree_depth,
            array[T.{pk}] AS tree_path,
            array[{order_by}] AS tree_ordering,
            T."{pk}"
        FROM {db_table} T
        WHERE T."{parent}" IS NULL

to:

SELECT
            0 AS tree_depth,
            array[T.{pk}] AS tree_path,
            array[{order_by}] AS tree_ordering,
            T."{pk}"
        FROM {db_table} T
        WHERE T."{parent}" in <<pks of queryset>>

but I also have no idea that this is feasible.

matthiask commented 2 years ago

This would certainly be doable; the problem is that django-tree-queries doesn't add much API at all. It has almost no extension points. What you write would probably be more straightforward to implement on top of a more generic solution for building CTEs, maybe https://pypi.org/project/django-cte/