Speed up your product imports
You can use these examples to speed up your product imports in Episerver Commerce.
Read my blog here
using System.Collections.Generic; | |
using System.Linq; | |
using EPiServer.Commerce.Catalog.ContentTypes; | |
using EPiServer.Core; | |
using EPiServer.Find; | |
using EPiServer.Find.Cms; | |
using EPiServer.PlugIn; | |
using EPiServer.Scheduler; | |
using EPiServer.ServiceLocation; | |
[ScheduledPlugIn(DisplayName = "Import products ScheduledJob", GUID = "617b1b46-c1d8-4a94-82fe-d7c689b9f7c0")] | |
public class ImportScheduledJob : ScheduledJobBase | |
{ | |
private readonly IContentIndexer contentIndexer; | |
private readonly IClient searchClient; | |
private bool stopSignaled; | |
/// <summary> | |
/// Initializes a new instance of the <see cref="ImportScheduledJob" /> class. | |
/// </summary> | |
/// <exception cref="ActivationException">if there is are errors resolving the service instance.</exception> | |
public ImportScheduledJob() | |
{ | |
this.IsStoppable = true; | |
this.searchClient = ServiceLocator.Current.GetInstance<IClient>(); | |
this.contentIndexer = ServiceLocator.Current.GetInstance<IContentIndexer>(); | |
} | |
/// <summary> | |
/// Called when a scheduled job executes | |
/// </summary> | |
/// <returns>A status message to be stored in the database log and visible from admin mode</returns> | |
public override string Execute() | |
{ | |
// Call OnStatusChanged to periodically notify progress of job for manually started jobs | |
this.OnStatusChanged(string.Format("Starting execution of {0}", this.GetType())); | |
// Turn off indexing events | |
EventedIndexingSettings.Instance.EventedIndexingEnabled = false; | |
EventedIndexingSettings.Instance.ScheduledPageQueueEnabled = false; | |
// Do your import | |
// Add new or changed products to this list. | |
IEnumerable<ProductContent> products = new List<ProductContent>(); | |
// Add the references of products that are removed to this list | |
IEnumerable<ContentReference> itemsToRemove = new List<ContentReference>(); | |
// You can use the content indexer or the search client to add/update your content in the index. See remarks on the methods for the differences. | |
AddToIndex(itemsToAdd: products, contentIndexer: this.contentIndexer); | |
// The content indexer does not provide a batch job, so better use the search client. | |
RemoveFromIndex(itemsToRemove: itemsToRemove, searchClient: this.searchClient); | |
// Turn on indexing events again. | |
EventedIndexingSettings.Instance.EventedIndexingEnabled = true; | |
EventedIndexingSettings.Instance.ScheduledPageQueueEnabled = true; | |
// For long running jobs periodically check if stop is signaled and if so stop execution | |
if (this.stopSignaled) | |
{ | |
return "Stop of job was called"; | |
} | |
return "Change to message that describes outcome of execution"; | |
} | |
/// <summary> | |
/// Determines whether this instance is stopped. | |
/// </summary> | |
/// <returns><c>true</c> if this instance is stopped; otherwise, <c>false</c>.</returns> | |
public bool IsStopped() | |
{ | |
return this.stopSignaled; | |
} | |
/// <summary> | |
/// Called when a user clicks on Stop for a manually started job, or when ASP.NET shuts down. | |
/// </summary> | |
public override void Stop() | |
{ | |
this.stopSignaled = true; | |
} | |
/// <summary> | |
/// Adds the selected items to the Find index. | |
/// </summary> | |
/// <param name="itemsToAdd">The items to add.</param> | |
/// <param name="contentIndexer">The content indexer.</param> | |
/// <remarks>Will add all language versions.</remarks> | |
private static void AddToIndex(IEnumerable<IContent> itemsToAdd, IContentIndexer contentIndexer) | |
{ | |
contentIndexer.Index( | |
contentItems: itemsToAdd, | |
options: new IndexOptions { IndexAllLanguageVersions = true }); | |
} | |
/// <summary> | |
/// Adds the selected items to the Find index. | |
/// </summary> | |
/// <param name="itemsToAdd">The items to add.</param> | |
/// <param name="searchClient">The search client.</param> | |
/// <remarks> | |
/// Will NOT add all language versions, you will need to add all content versions you want to index to the | |
/// <paramref name="itemsToAdd" /> | |
/// </remarks> | |
private static void AddToIndex(IEnumerable<object> itemsToAdd, IClient searchClient) | |
{ | |
searchClient.Index(objectsToIndex: itemsToAdd); | |
} | |
/// <summary> | |
/// Removes the selected items from the Find index. | |
/// </summary> | |
/// <param name="itemsToRemove">The items to remove.</param> | |
/// <param name="searchClient">The search client.</param> | |
private static void RemoveFromIndex(IEnumerable<IContent> itemsToRemove, IClient searchClient) | |
{ | |
RemoveFromIndex(itemsToRemove.Select(p => p.ContentLink), searchClient: searchClient); | |
} | |
/// <summary> | |
/// Removes the selected items from the Find index. | |
/// </summary> | |
/// <param name="itemsToRemove">The items to remove.</param> | |
/// <param name="searchClient">The search client.</param> | |
private static void RemoveFromIndex(IEnumerable<ContentReference> itemsToRemove, IClient searchClient) | |
{ | |
FilterBuilder<CatalogContentBase> deleteFilterBuilder = | |
new FilterBuilder<CatalogContentBase>(client: searchClient); | |
deleteFilterBuilder = itemsToRemove.Aggregate( | |
seed: deleteFilterBuilder, | |
func: (current, contentLink) => current.Or(x => x.ContentLink.Match(contentLink))); | |
searchClient.Delete<CatalogContentBase>(filter: deleteFilterBuilder); | |
} | |
} |