Get items - the fastest way

Reading time ~5 minutes

What is the fastest way of getting items? I present you 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).

At 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"]

I was scared of:

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 test to find our what’s the truth.

Test Data

First I had to create some test data:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$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).

1
2
3
4
5
6
7
8
9
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;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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;
}
1
2
3
4
5
6
public static Result SelectItems(Item item, string query)
{
    var r = new Result();
    r.Items.AddRange(item.Axes.SelectItems(query));
    return r;
}
1
2
3
4
5
6
7
8
9
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;
}
1
2
3
4
5
6
public static Result SelectItem(Item item, string query)
{
    var r = new Result();
    r.Items.Add(item?.Axes.SelectSingleItem(query));
    return r;
}
1
2
3
4
5
6
7
8
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;
}
1
2
3
4
5
6
7
8
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;
}
1
2
3
4
5
6
public static Result SelectItemFast(Item item, string query)
{
    var r = new Result();
    r.Items.Add(item.Database.SelectSingleItem(query));
    return r;
}
1
2
3
4
5
6
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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
$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)

Results

Average time - no cache

Function Time [ms] Items
GetDescendants - AllItems 536.63012 1110
Children - Recursive 650.29703 1110
Select Items - AllItems 693.7084 1110
Select Items Fast - AllItems 694.98984 1110
Children - SingleLevel 12.03793 10
Select Items - SingleLevel 21.28989 10
GetDescendants - SingleItem 493.75492 1
Children - SingleItem 14.90564 1
Single Item - SingleLevel 23.95847 1
Single Item - Recursive 25.20063 1
Single Item Fast - SingleItem 26.48343 1
Single Item Fast - Recursive 35.9395 1

Average time - with cache

Function Time [ms] Items
GetDescendants - AllItems 16.84566 1110
Children - Recursive 24.65328 1110
Select Items - AllItems 94.90774 1110
Select Items Fast - AllItems 32.19504 1110
Children - SingleLevel 0.20868 10
Select Items - SingleLevel 0.64663 10
GetDescendants - SingleItem 16.11355 1
Children - SingleItem 0.16458 1
Single Item - SingleLevel 0.26698 1
Single Item - Recursive 0.25829 1
Single Item Fast - SingleItem 11.44318 1
Single Item Fast - Recursive 22.55551 1

Summary

My goal was to answer my question not test Sitecore speed. I wanted to know whether using InheritsFrom instead of putting conditions in query isn’t slower.

It turned out that there is noting to be afraid of. Actually the new method is even faster than the old one.

EDIT 2020:

You can find the whole code here

Asset Optimizer configuration

Explanation of different configuration options of SXA Asset Optimizer Continue reading

Items as resources and Unicorn

Published on November 21, 2021

Sitecore Extensions version 3.4 released

Published on November 07, 2020