How to populate sitecore with test data using power shell

Sometimes you need to generate test data, for instance, to check the performance of a solution. In sitecore you can do that in different ways: you can create items by hand, which is obviously the worst option, you can write a piece of code that will create items for you and you can also use the sitecore power shell.

Following script creates 10,000 test items in a loop, sets the name, price and category for each of them. In addition at the end, it displays the total run time.

cd master:\\content\home\products
$categories = @{$true="{050E9CAE-F55F-4DF1-8001-07CD4D70FC09}";$false="{8C11E41B-0CE5-4602-BB6D-5DE5B4356D25}"}

$stopwatch = new-object -type 'System.Diagnostics.Stopwatch'
$stopwatch.Start()

for($i = 1; $i -le 10000; $i++)
{
    $newProduct = new-item "product_$i" -type 'sample\product'
    
    set-itemproperty $newProduct.Paths.Path -name "Name" -value "Product $i"
    set-itemproperty $newProduct.Paths.Path -name "Price" -value $i
    set-itemproperty $newProduct.Paths.Path -name "Category" -value $categories[$i % 2 -eq 1]
}

$stopwatch.Stop()
write-host $stopwatch.Elapsed

Performance

On my test virtual machine, for 10,000 of items, the script executes from about an hour to one and a half hour. It is quite a lot of time if you want to insert a milion of products. That is why I introduced two optimizations. Here they are:

cd master:\\content\home\products

$categories = @{$true="{050E9CAE-F55F-4DF1-8001-07CD4D70FC09}";$false="{8C11E41B-0CE5-4602-BB6D-5DE5B4356D25}"}

$stopwatch = new-object -type 'System.Diagnostics.Stopwatch'
$stopwatch.Start()

$bulkupdate = new-object -type "Sitecore.Data.BulkUpdateContext"

for($i = 1; $i -le 10000; $i++)
{
    $newProduct = new-item "product_$i" -type 'sample\product'
    
    $newProduct.Editing.BeginEdit();
    $newProduct["Name"] = "Product $i";
    $newProduct["Price"] = $i;
    $newProduct["Category"] = $categories[$i % 2 -eq 1] ;
    $newProduct.Editing.EndEdit() | out-null
}

if($bulkupdate -ne $null)
{
    $bulkupdate.Dispose();
}

$stopwatch.Stop()
write-host $stopwatch.Elapsed

The use of the BulkUpdateContext forces Sitecore to not update index immediately after every save, but to do it later.

By avoiding the use of cmdlet Set-Property for the direct Sitecore API calls we managed to reduce the number of BeginEdit() and EndEdit() method calls three times, becuse Set-Property internally calls this methods every time.

Thanks to this optimizations I was able to reduce the time of inserting 10,000 items to about 25 minutes.

A small note:
If you are using Power Shell that is included in Sitecore Rocks, this optimizations will not work. This Power Shell is not running in application context so you need to use cmdlets.