Get items-The Fastest way in Sitecore

What is the fastest way of getting items? I present you the results of my experiment performed some time ago.


Introduction

Maybe the title is a little bit misleading because finding the fastest way of getting items was not my goal (you can find an answer for this too).

In the early days of Helix and Habitat, I was wondering if recommended approach for getting items with additional filtering is not a step back in terms of speed.

Previously I was using in my opinion the fastest way - queries

fast:/sitecore/content/*[@@templatename="TestItem"]

fast:/sitecore/content/*[@@templatename="TestItem"]
I was scared of:

item.Children.Where(i => TemplateManager.GetTemplate(i).InheritsFrom(id))

item.Children.Where(i => TemplateManager.GetTemplate(i).InheritsFrom(id))
I thought that it cannot be faster, but is what’s the truth?

Experiment

I decided to perform a test to find out what’s the truth.

Test Data

First I had to create some test data:

$root = "/sitecore/content"
$testTemplate = New-Item -Path "/sitecore/templates/User Defined" -Name "TestItem" -ItemType "System/Templates/Template"
[Sitecore.Data.Items.TemplateItem]$testTemplate = $testTemplate
$performanceRoot = New-Item -Path $root -Name "PerformanceRoot" -ItemType $testTemplate.FullName

0..9 | % {
    $level0 = $_
    $level0Parent = New-Item -Parent $performanceRoot -Name $level0 -ItemType $testTemplate.FullName
    0..9 | % {
        $level1 = $_
        $level1Parent = New-Item -Parent $level0Parent -Name $level1 -ItemType $testTemplate.FullName
        0..9 | % {
            $level2 = $_
            New-Item -Parent $level1Parent -Name $level2 -ItemType $testTemplate.FullName | Out-Null
        }
    }
}

In content tree it looked like this:


Logic:

After I had my items ready in database I wrote different function for obtaining items.
Note that that filtering in some methods is different than recommended by Helix (checking template name instead of inheritance)

public static Result GetDescendantsAllItems(Item item, ID id)
{
    var r = new Result();
    var items = item.Axes.GetDescendants()
            .Where(i => TemplateManager.GetTemplate(i).InheritsFrom(id))
            .ToList();
    r.Items.AddRange(items);
    return r;
}

public static Result ChildrenRecursive(Item item, ID id)
{
    var r = new Result();
    var itemChildren = item.Children;
    var items = itemChildren
            .Where(i => TemplateManager.GetTemplate(i).InheritsFrom(id))
            .ToList();
    r.Items.AddRange(items);
    foreach (Item i in itemChildren)
    {
        if (i.HasChildren)
        {
            r.Items.AddRange(ChildrenRecursive(i, id).Items);
        }
    }
    return r;
}

public static Result SelectItems(Item item, string query)
{
    var r = new Result();
    r.Items.AddRange(item.Axes.SelectItems(query));
    return r;
}

public static Result ChildrenSingleLevel(Item item, ID id)
{
    var r = new Result();
    var items = item.Children
            .Where(i => TemplateManager.GetTemplate(i).InheritsFrom(id))
            .ToList();
    r.Items.AddRange(items);
    return r;
}


Note: "SelectSingleItem(query)" it will always works with string type query param



public static Result SelectItem(Item item, string query)
{
    var r = new Result();
    r.Items.Add(item?.Axes.SelectSingleItem(query));
    return r;
}

public static Result GetDescendantsSingleItem(Item item, ID id)
{
    var r = new Result();
    var items = item.Axes.GetDescendants()
            .FirstOrDefault(i => TemplateManager.GetTemplate(i).InheritsFrom(id));
    r.Items.Add(items);
    return r;
}

 public static Result ChildrenSingleItem(Item item, ID id)
{
    var r = new Result();
    var items = item.Children
            .FirstOrDefault(i => TemplateManager.GetTemplate(i).InheritsFrom(id));
    r.Items.Add(items);
    return r;
}

 public static Result SelectItemFast(Item item, string query)
{
    var r = new Result();
    r.Items.Add(item.Database.SelectSingleItem(query));
    return r;
}

 public static Result SelectItemsFast(Item item, string query)
{
    var r = new Result();
    r.Items.AddRange(item.Database.SelectItems(query));
    return r;
}

Tests:

After everything was setup I performed tests. I used something like this:


 $item = Get-Item .
$templateItem = Get-Item -Path "/sitecore/templates/User Defined/TestItem"

Write-Host "`nGetDescendants - AllItems" -ForegroundColor Green
$GetDescendantsAllItems = [Tests]::GetDescendantsAllItems($item, $templateItem.ID)
$GetDescendantsAllItems.Items.Count
$GetDescendantsAllItems.TimeSpan.TotalMilliseconds

Write-Host "`nChildren - Recursive" -ForegroundColor Green
$ChildrenRecursive = [Tests]::ChildrenRecursive($item, $templateItem.ID)
$ChildrenRecursive.Items.Count
$ChildrenRecursive.TimeSpan.TotalMilliseconds

Write-Host "`nSelect Items - AllItems" -ForegroundColor Green
$SelectItemsRecursive = [Tests]::SelectItems($item, ".//*[@@templatename='TestItem']")
$SelectItemsRecursive.Items.Count
$SelectItemsRecursive.TimeSpan.TotalMilliseconds

Write-Host "`nSelect Items Fast - AllItems" -ForegroundColor Green
$query = "fast:$($item.Paths.path)//*[@@templatename='TestItem']"
$SelectItemsFast = [Tests]::SelectItemsFast($item, $query, $count)
$SelectItemsFast.Items.Count
$SelectItemsFast.TimeSpan.TotalMilliseconds

Write-Host "`nChildren - SingleLevel" -ForegroundColor Green
$ChildrenSingleLevel = [Tests]::ChildrenSingleLevel($item, $templateItem.ID)
$ChildrenSingleLevel.Items.Count
$ChildrenSingleLevel.TimeSpan.TotalMilliseconds

Write-Host "`nSelect Items - SingleLevel" -ForegroundColor Green
$SelectItems = [Tests]::SelectItems($item, "./*[@@templatename='TestItem']")
$SelectItems.Items.Count
$SelectItems.TimeSpan.TotalMilliseconds

Write-Host "`nGetDescendants - SingleItem" -ForegroundColor Green
$GetDescendantsSingleItem = [Tests]::GetDescendantsSingleItem($item, $templateItem.ID)
$GetDescendantsSingleItem.Items.Count
$GetDescendantsSingleItem.TimeSpan.TotalMilliseconds

Write-Host "`nChildren - SingleItem" -ForegroundColor Green
$ChildrenSingleItem = [Tests]::ChildrenSingleItem($item, $templateItem.ID)
$ChildrenSingleItem.Items.Count
$ChildrenSingleItem.TimeSpan.TotalMilliseconds

Write-Host "`nSingle Item - SingleLevel" -ForegroundColor Green
$SelectItem = [Tests]::SelectItem($item, "./*[@@templatename='TestItem']")
$SelectItem.Items.Count
$SelectItem.TimeSpan.TotalMilliseconds

Write-Host "`nSingle Item - Recursive" -ForegroundColor Green
$SelectItemRecursive = [Tests]::SelectItem($item, ".//*[@@templatename='TestItem']")
$SelectItemRecursive.Items.Count
$SelectItemRecursive.TimeSpan.TotalMilliseconds

Write-Host "`nSelect Item Fast - SingleItem" -ForegroundColor Green
$query = "fast:$($item.Paths.path)/*[@@templatename='TestItem']"
$SelectItemFast = [Tests]::SelectItemFast($item, $query, $count)
$SelectItemFast.Items.Count
$SelectItemFast.TimeSpan.TotalMilliseconds

Write-Host "`nSelect Item Fast - AllItems" -ForegroundColor Green
$query = "fast:$($item.Paths.path)//*[@@templatename='TestItem']"
$SelectItemFast = [Tests]::SelectItemFast($item, $query, $count)
$SelectItemFast.Items.Count
$SelectItemFast.TimeSpan.TotalMilliseconds


If you are going to try it by yourself ::
Don’t forget to update the max number of items in a query result set (Query.MaxItems)


Note: How we can call c# code Action method from poershell script.

Comments