Best way to get some documents in code? .Descendants<T>, .Children(), some othe way?

I am writing a help file that will either return the last 5 published blog posts or all the blog posts published in a given month. Back in v13 I used this:

		public IEnumerable<(NewsArticle article, string articleSnipplet, string articleAuthor)> GetArticlesByCount(IPublishedContent siteRootNode, int displayAmount)
		{
			var articles = siteRootNode.Descendants<NewsArticle>().OrderByDescending(x => x.CreateDate).Take(displayAmount);
			return ProcessArticles(articles);
		}

		public IEnumerable<(NewsArticle article, string articleSnipplet, string articleAuthor)> GetArticlesByMonthYear(IPublishedContent siteRootNode, int month, int year)
		{
			var articles = siteRootNode.Descendants<NewsArticle>().Where(x => x.CreateDate.Year == year && x.CreateDate.Month == month).OrderByDescending(x => x.CreateDate);
			return ProcessArticles(articles);
		}

I can’t find any information in the docs on .Descendants() and I honstily don’t remember how I came up with that method almost 4 years ago.

So I decided to check with Claud on if that is the best way and see what it recomends and investigate from there. And well, Claud is an idiot in this case. It took a lot of yelling at it to event admit I was using .Descendants<T>() in the first place. I did finally get it to give me two suggestions.

[Note, the content structure is Root → NewsHome → YearNode → MonthNode → NewsArticle]
1: Somehow traverse down the node system from the passed in root to the MonthNode and then call .Children() on it.
2: use .DescendantsOfType() but I can’t find anything at docs.umbraco.com so I can’t even verify that it is even a real mothod to use.

I know my existing way works but the problem is I am not sure if it is efficant. Some sites will end up having hundreds of thousands of content nodes nodes in them.

So, what is the best, and most efficient, way for me to be pulling these content nodes so I can display summaries on different pages?

Hi @Eaglef90

You need to be careful using any of the Descendants methods as they can have performance implications as they query the whole hierarchy from your current node. If you have thousands of nested child nodes they will all get evaluated. It is mentioned here:

If you need to filter children then just use Children with a suitable where clause, or even better, use the Examine index as that will be fastest of all (but would require more re-work to your code and indexes can get out of sync so don’t rely on them for anything important).

For your needs, walking down the heirarchy from the root, specifying the exact type/filter you need until you get the container for your articles, and then call Children and filter them from there.

If you want to filter by type, use ChildrenOfType() which would be quicker as Umbraco will filter this to only query the type you specify rather than returning all for you to filter.

Justin

So to traverse to the news node I would do siteRootNode.ChildrenOfType("newsHome") and work my logic for figuring out the right year and then month folders from there? Just want to make sure my traversal is correct before I dive back into the code.

Hi @Eaglef90

That would be correct, although if your blog posts are in year and month folders you would need iterate over all of them and concatenate the results - I think maybe in your specific use-case using DescendantsOfType<T>() may be your best option as long as you understand the potential risks and performance implications vs having to re-work and end up with more complex code.

Justin

The content structure is Root → NewsHome → YearNode → MonthNode → NewsArticle

So if I do something like

var newsRoot = siteRootNode.ChildrenOfType("newsHome")

Then I just have to do the logic to get the needed year and month and traverse down as needed and I can avoid the performance hit of the Descendants call.

I am re-writting this code from scratch as part of a ground up rebuild so I don’t mind re-working things. I do know goign to the Exame Index is not reliable for this as if the index gets out of sync or is rebuilding for some reason then I will not get the right artciles, if any, back.

Hi @Eaglef90

You could use something like this:

public class NewsHelper
{
    private readonly INavigationQueryService _nav;
    private readonly IPublishedStatusFilteringService _filter;

    public NewsHelper(INavigationQueryService nav, IPublishedStatusFilteringService filter)
    {
        _nav = nav;
        _filter = filter;
    }

    public IEnumerable<NewsArticle> GetArticlesByCount(IPublishedContent siteRootNode, int displayAmount)
    {
        var newsHome = siteRootNode.ChildrenOfType(_nav, _filter, "newsHome").FirstOrDefault();
        if (newsHome is null) return Enumerable.Empty<NewsArticle>();

        return newsHome.ChildrenOfType(_nav, _filter, "yearNode")
            .OrderByDescending(y => y.Name)
            .SelectMany(y => y.ChildrenOfType(_nav, _filter, "monthNode").OrderByDescending(m => m.SortOrder))
            .SelectMany(m => m.Children<NewsArticle>(_nav, _filter))
            .OrderByDescending(a => a.CreateDate)
            .Take(displayAmount);
    }

    public IEnumerable<NewsArticle> GetArticlesByMonthYear(IPublishedContent siteRootNode, int month, int year)
    {
        var monthNode = siteRootNode.ChildrenOfType(_nav, _filter, "newsHome")
            .SelectMany(n => n.ChildrenOfType(_nav, _filter, "yearNode"))
            .Where(y => y.Name == year.ToString())
            .SelectMany(y => y.ChildrenOfType(_nav, _filter, "monthNode"))
            .FirstOrDefault(m => m.SortOrder == month - 1);

        return monthNode?.Children<NewsArticle>(_nav, _filter)
            .OrderByDescending(a => a.CreateDate)
            ?? Enumerable.Empty<NewsArticle>();
    }
}

Justin

Thanks for the code sample. I have just sat down with the intent to start reworking my code with the new information I have and this will help a lot.

What is the INavigationQueryService and IPublishedStatusFilteringService? I can’t find these in the docs. Is there some other place I should be looking?

Hi @Eaglef90

There are some more friendly extensions so you should be able to use this instead (I think AI overcomplicated the example!)

using Umbraco.Cms.Core.Models.PublishedContent;
using Umbraco.Cms.Web.Common.PublishedModels;
using Umbraco.Extensions;

public class NewsHelper
{
    public IEnumerable<NewsArticle> GetArticlesByCount(IPublishedContent siteRootNode, int displayAmount)
    {
        var newsHome = siteRootNode.FirstChildOfType("newsHome");
        if (newsHome is null) return Enumerable.Empty<NewsArticle>();

        return newsHome.ChildrenOfType("yearNode")
            .OrderByDescending(y => y.Name)
            .SelectMany(y => y.ChildrenOfType("monthNode").OrderByDescending(m => m.SortOrder))
            .SelectMany(m => m.Children<NewsArticle>())
            .OrderByDescending(a => a.CreateDate)
            .Take(displayAmount);
    }

    public IEnumerable<NewsArticle> GetArticlesByMonthYear(IPublishedContent siteRootNode, int month, int year)
    {
        var monthNode = siteRootNode.FirstChildOfType("newsHome")?
            .ChildrenOfType("yearNode")
            .FirstOrDefault(y => y.Name == year.ToString())?
            .ChildrenOfType("monthNode")
            .FirstOrDefault(m => m.SortOrder == month - 1);

        return monthNode?.Children<NewsArticle>()
            .OrderByDescending(a => a.CreateDate)
            ?? Enumerable.Empty<NewsArticle>();
    }
}

Justin

Thanks for the updated code. AI does have a tendency to overcomplicate things. I appreacte the help on this but may I also ask that in the feature if your using AI please mention so? It follows the guidelines of the forums and it helps people make more informed choices around where to move next. I personaly don’t mind it, heck this tread started cause AI got me confused on things. But when I know it is AI code vs someone’s own code from there experiance I tend to scruitnize the AI code closer then normal.

I am about to go to bed and starting my week long vacation so I am not sure if I will get to starting up the news code again soon. This codebase will help out at lot when I do so thank you again for your help.

Hi @Eaglef90

Of course, I should have mentioned the code was AI - apologies about that.

Justin