Smart recommendations for Commerce PT2

A little while ago I wrote a post about how you could use the  Recommendations API from Microsoft Cognitive Services to give your visitors smart recommendations. This time, as promised, I will go into the details of my implementation a bit.

I must say that I am quite content with the results of the recommendations that are returned. If you have used it, let me know how what you think of the Recommendations API. I also think the pricing is quite OK. 10.000 transactions for free, a 100.000 for $75

Anyway, the project consists of a core library and two scheduled jobs. One scheduled job to export your catalog items, one to export the usage statistics and trigger a build.

The core library contains all the models, a wrapper for the API calls a helper with some methods to adapt commerce content to content that the API can use and a base RecommendationService.

This RecommendationService is a base class, you will need to do your own implementation in your commerce project, as no catalog is the same. If you don’t override the methods, you have the basic version, without feature in your model e.g. The service is used by the jobs to send the catalog and usage stats, and you can use it to get the recommendations for a user of for an item.

The most basic implementation you can do is

[ServiceConfiguration(typeof(IRecommendationService), Lifecycle = ServiceInstanceScope.Singleton)]
    public class QuicksilverRecommendationService : RecommendationService
    {
        public QuicksilverRecommendationService(
            RecommendationsApiWrapper recommendationsApiWrapper, 
            RecommendationSettingsRepository recommendationSettingsRepository, 
            IContentLoader contentLoader, 
            ReferenceConverter referenceConverter,
            IOrderRepository orderRepository, 
            ILogger log )
            : base(
                recommendationsApiWrapper, 
                recommendationSettingsRepository, 
                contentLoader, 
                referenceConverter, 
                orderRepository, 
                log)
        {
        }
    }
}

This will give you the following methods to override

List<CatalogItem> GetCatalogItems(DateTime since);
List<EntryContentBase> GetItemRecommendations(string itemIds);
List<T> GetItemRecommendations<T>(string itemIds);
List<EntryContentBase> GetItemRecommendations(string itemIds, int numberOfResults);
List<T> GetItemRecommendations<T>(string itemIds, int numberOfResults);
List<UsageItem> GetUsageItems(DateTime since);
List<EntryContentBase> GetUserRecommendations();
List<T> GetUserRecommendations<T>();
List<EntryContentBase> GetUserRecommendations(int numberOfResults);
List<T> GetUserRecommendations<T>(int numberOfResults);
void SendUsageEvent(int quantity, string code, decimal unitPrice, EventType eventType);
void SendUsageEvent(decimal quantity, string code, decimal unitPrice, EventType eventType);

In Quicksilver e.g. you could get the CatalogItems like this

public override List<CatalogItem> GetCatalogItems(DateTime since)
        {
            List<CatalogItem> catalogItems = new List<CatalogItem>();

            IEnumerable<ContentReference> descendents =
                this.ContentLoader.GetDescendents(this.ReferenceConverter.GetRootLink());

            foreach (ContentReference contentReference in descendents)
            {
                FashionVariant variation;

                if (!this.ContentLoader.TryGet(contentReference, out variation))
                {
                    continue;
                }

                if (variation.Created < since)
                {
                    continue;
                }

                NodeContent node =
                    this.ContentLoader.GetAncestors(contentReference).OfType<NodeContent>().FirstOrDefault();

                CatalogItem catalogItem = new CatalogItem
                                              {
                                                  ItemID = variation.Code,
                                                  ItemName = variation.Name,
                                                  ProductCategory = node == null ? "undefined" : node.Name,
                                                  Description = string.Empty,
                                                  Features = new Dictionary<string, string>() { { "color", variation.Color }, { "size", variation.Size } }
                                              };

                catalogItems.Add(catalogItem);
            }

            return catalogItems;
        }

The GetCatalogItems method in the base just gets all variations. If you don’t want to enrich your model with features, you can leave it as is. To add features to your model you can add them like I did above. For all the base methods in the service, have a look on GitHub.

 

You also can/need to configure some things. For now in the appSettings.

Add an account key to your appsettings: <add key=”recommendations:accountkey” value=”YourKey” />

If you want to use the ‘Frequently Bought Together build’ change the key in the appSettings: <add key=”recommendations:useftbbuild” value=”true” />.

If the baseuri for the API changes, update the key in the appSettings: <add key=”recommendations:baseuri” value=”https://westus.api.cognitive.microsoft.com/recommendations/v4.0″ />

If you want to use a different model name, update the key in the appSettings: <add key=”recommendations:modelname” value=”EPiServerCommerce” />

If you want to use a different display name for the catalog, update the key in the appSettings: <add key=”recommendations:catalogdisplayname” value=”EPiServer Commerce catalog” />

If you want to use a different display name for the usages, update the key in the appSettings: <add key=”recommendations:usagedisplayname” value=”EPiServer Commerce catalog usages” />

The Catalog export job creates the model for you if there is no model yet and uses the RecommendationService to get the items and sends them to the API.

The Usage export job uses the RecommendationService to get the usage statistics ands sends them to the API. It also triggers a build to start the “learning process” of your model.

All code is on GitHub as always. You can get the NuGet packages or zipped files from the releases tab, or from MyGet.

Am not quite sure if I should put this on the EPiServer NuGet feed, as it’s quite experimental. Let me know if you would like that.

Next up will be that you don’t have to make a choice between a recommendations build or a FBT build. And of course creating business rules for your model from within EPiServer.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s