Best way to fetch a manually sorted content tree

I have a multi level content tree, manually sorted in Content Studio. I want to display roughly the same tree, in the same order in a part.

Do I need to write my own recursive function for this, using contentLib.getChildren without any sort param? Or is it in any way possible to achieve this with contentLib.query?

I’ve been playing with query for a while, and I’ve not been able to produce anything useful. _manualordervalue doesn’t seem to play well in a multi level deep path based query. Also, getChildren isn’t particularly recursive or able to filter on content types, but at least I get the content in the correct order with this one.

I did find a couple of threads with similar issues in the forums, but no luck there either. Seems I’m stuck with the rather simple getChildren, and rolling my own recursion and filtering?

Performance-vice, is the getChildren method cheap?

1 Like

First things first, yes getChildren is cheap, but you could also use query, specifying parent as parameter, or even filtering on the path. Check out lib-menuy https://github.com/enonic/lib-menu/blob/master/src/main/resources/site/lib/enonic/menu/index.js - gode that builds a menu from the tree structure. I guess it should be rewritten to use query in order to optimize it.

1 Like

Thanks. I will definitely try the parent param with query. I didn’t find any mention of this in the doc. If it will keep the sorting, and provide filtering, it might be exactly what I need.

So far, I ended up with this recursive thing.

function fetchTree(content) {

    if (content.hasChildren) {

        var children = contentLib.getChildren({
            key:content._path,
            count:9999
        });

        if (children.count) {
            content.children = [];
            children.hits.map(function(item) {
                var nextLevel = fetchTree(item);
                // Only add the stuff I need
                if ([app.name + ':some-content-type',
                     app.name + ':some-other-content-type'].indexOf(nextLevel.type) > -1) {
                    content.children.push(nextLevel);
                }
            });
        }

    }

    return content;
}

Update: Edited above code to get app name from global vars

Update: Above code improved slightly by using the hasChildren attribute that is present on all content.

I also tried adding a parent param to query, but it’s just ignored. Perhaps that wasn’t what you meant? Did you mean this:

var result = contentLib.query({
    count: 9999,
    query: "_parentPath like '/content/my/content/path*'"
});

Unfortunately it doesn’t keep the manual sorting order.

1 Like

Also, a best-practice is to never write the app-name inside your code, but resolve this from the context. As this will make things easier to maintain and potentially also re-use.

2 Likes

The getChildren is basically a normal query, setting the parent-expression and fetches the child-order from the node instead of giving it in the query.

So, to mimic a getChildren-query, you would have to do it like this:

var result = contentLib.query({
    count: 9999,
    query: "_parentPath = '/content/my/content/path'"
    sort: "_manualOrderValue DESC",
});

This will give you all children ordered by manual order value for that parent. Using the manualOrderValue on several parents or levels does not make sense, since its an order value within a specific parent, so to do recursive, you will have to do this query for each level, or stick with the getChildren. Performance-wise there is no difference.

3 Likes

I was more thinking _parentPath = ‘/content/my/parent’ and then sort the result by _manualOrderValue

Searching all children of an item recursively will obviously produce a chaotic result when sorted by _manualOrder

Yep. I was kind of hoping it would be magically handled by some backend. But I got a good explanation to the inner workings by @rmy . Thanks to the both of you!