Tuesday, March 28, 2017

Incorporating xDB analytics information into SXA

Sitecore Hackathon 2017

One of the possible categories for the 2017 Sitecore Hackathon was the Sitecore Experience Accelerator (SXA). With our "No Weekend 4 Us" team we took to this challenge and created a custom component that works with SXA and the xDB (and did other fancy stuff, but that is explained in other blog posts).

Different aspects of xDB

xDB obviously is huge. And it is often that huge potential that tends to scare off those who attempt to venture into the first steps that lead into harnessing xDB's true potential.

For our entry into the SxA part of the Hackathon, we decided to go for some quick integrations that would help us showcase some of the currently missing interactions that are possible.

We used the following 3 aspects:
  • Content provided based on previous visits
  • Creation and managing Favorite content
  • Sorting content based of the accumulated pattern cards.

Content based on your visists 

First step is to load all information of your current session/interactions. This is simple to achieve, by getting the tracker (if active, otherwise change those settings or this will be very hard :p) and retrieving the assorted current Interaction Pages.

What information can be easily retrieved or added programmatically is listed here: https://doc.sitecore.net/sitecore_experience_platform/developing/marketing_operations/analytics_tracking

After that it is time to retrieve the interaction history based off the same ContactId and merging these two together. We chose to provide a max number of days to go back into the history as well as a max number of visits we wanted to load from the contactRepository.

            var tracker = Tracker.Current;
            if (tracker == null || !tracker.IsActive)
                return Enumerable.Empty<Guid>();

            var currentPages = tracker.Interaction.Pages.GroupBy(p => p.Item.Id).Select(g => g.Key);

            var maxDate = DateTime.Now;
            var minDate = maxDate.AddDays(-MaxDays);

            var contactRepository = Factory.CreateObject("tracking/contactRepository", true) as ContactRepository;
            if (contactRepository == null)
                return Enumerable.Empty<Guid>();
            var interactions = contactRepository.LoadHistoricalInteractions(tracker.Contact.ContactId, MaxVisits, minDate, maxDate).ToArray();
            var previousPages = interactions.SelectMany(i => i.Pages).GroupBy(p => p.Item.Id).Select(g => g.Key);

            return currentPages.Concat(previousPages).Distinct();

Simply mix and stir to get a good Interaction cocktail of recent and previous logged interactions. 
So, very quick and clean code to get this information loaded and prepped for HTML or rendering manipulation through visualization or the actual order of your elements.

Favorite content

Time to allow for our visitors to be able to tag content as one of their favorite pieces of content.
Typically, we used to persist this on cookie level, or work our way through local storage and in some very rare cases we would persist this on a user's profile.

Come to think of it, neither of these approaches were ideal. Local storage and cookies will differ depending on the physical location the visitor is in (different device) so that is a no-go for most business requirements. And storing this directly on the profile makes the actual profile too heavy and polluted... Enter the Sitecore experience database...

So, as this interaction is indeed coupled to a visitor, but not necessarily his/her profile, it actually makes perfect sense to have this information stored on the tracked contact.

Sitecore allows for a number of solutions that may or may not suit your need.
Either through:
  • Custom Tags
  • Custom Extensions
  • Custom of ootb Facets

Custom tags allow for a contact to become tagged with a status as well as a time aspect. In doing so, you are able to find/search through all contacts that have that tag applied. You simply get your contact from the Tracker and either set or remove the desired tag as such:

contact.Tags.Set("FavoriteTag", whateverValue); 

However, we have no need for the timestamp aspect and want to store lists of data for example.

Custom Extensions are defined as a simple and easy way to stick any kind of custom data into an xDB contact without going through a lot of custom configuration. It is realized by registring a key/value pair into the SimpleValues property.

The code for this is again very simple:

            var tracker = Tracker.Current;
            if (tracker == null || !tracker.IsActive || tracker.Contact == null)
                return new List<string>();

            var currentFavoriteBlogs = tracker.Contact.Extensions.SimpleValues[FavoriteKey];
            return string.IsNullOrEmpty(currentFavoriteBlogs) ? new List<string>() : currentFavoriteBlogs.Split('|').ToList();

Allowing us to let a user define content as a favorite through the frontend. Where the actual javascript that performed the post action is actually also embedded through scripts in the media library with SxA.

Custom or ootb Facets are the all-out solution for structuring contact data with a complex object. As this functionality seems over the top for a requirement as basic as ours we decided to hold back on this.

Pattern cards

Since we wanted content to become self-aware through the interaction behavior of the visitor, we needed to track what type of visitor we had on hand.
We went forward and created a specific Profile to which we could add our specific Pattern Cards and Profile Patterns.

Once created and assigned, we were able to go forward and write the code needed to find out our contacts derived pattern card after a number of interactions.

            var myProfile = Tracker.Current.Contact.BehaviorProfiles.Profiles.FirstOrDefault(p => p.Id == new ID(blogInterestItemId));
            if (myProfile != null && !ID.IsNullOrEmpty(myProfile.PatternId))
                var patternCard = Sitecore.Context.Database.GetItem(myProfile.PatternId);
                return patternCard;

Once this information was available, we could start showing content that would be of interest to our current visitor. Same type of FAQ content, blog entries ... and so on.

However, it isn't as simple as that... Where you tag content with Profile Patterns, the visitor gets assigned a specific (or a number of) Pattern Cards by clicking over tagged content and in turn gathering value points for specific aspects of those profile cards. Over time, the visitor will be 'best-matched' to Pattern Cards.
But that doesn't solve our issue. Because now we want to show content that is tagged with the same Profile patterns that are relevant to our visitor. But since Profile cards and Pattern cards can be quite different we would need to make assumptions based on the profile keys alone... And that would be too risky.

So went ahead and used Sitecore's tagging mechanism in the _semantics field to help us out. This allowed us to tag an actual assigned Pattern Card against a piece of content.

            const string SemanticsField = "__semantics";
            if (blogItem != null && !string.IsNullOrEmpty(currentPatternCard?.Fields[SemanticsField]?.Value))
                var tag = currentPatternCard.Fields[SemanticsField].Value.Split('|').FirstOrDefault();
                return tag != null && blogItem.Fields[SemanticsField].Value.Contains(tag);

            return false;

Ideally, this could have been done by using the same approach across Pattern Cards and Profile Cards but given the amount of content that otherwise would need to be checked against the link database versus the content we had on hand when constructing our SXA overview, this was the more obvious choice.

All in all, it was interesting to see how easy and straight-forward it was to incorporate xDB analytics information into the actual site behavior. And once you get this foundation set, you are good to go explore deeper and deeper into the rabbits hole.

No comments:

Post a Comment