Sitecore Sunday Pt 2: Programmatic Profile Association

Sitecore profiles and content tagging offer an interesting mechanism to implicitly understand the profiles your site visitors belong to, the journeys they take and how they behave differently to other users. For more information on the concept of profiles take a look at this post by nonlinear’s Amanda Shiga.

Where the tagging of content items with profiles can seem redundant is when profiles map 1 to 1 with taxonomies that already exist in your sites architecture, for example:

  • The content they are viewing is already tagged with controlled vocabulary e.g. luxury
  • The content they are viewing exists within a specific information architecture e.g. the luxury section
  • There are properties of the content they are viewing that identify the type of customer they are e.g. a price banding that indicates luxury

This is especially true for sites with thousands of pages and/or products. Who is going to tag all that content/data again?

In this post I want to explore technical solutions to automating the process of associating profiles to your site visitors without the upfront and ongoing tagging using the very taxonomies mentioned above.

Yo Dawg, I'm Implicitly, Implicitly Profiling

The first thing to look at is how does Sitecore traditionally associate profiles and profile scores with a users contact record (xDb contact that is) when they view an item tagged with a profile? It starts in the startTracking pipeline (Sitecore.Analytics.Tracking.config):


<startTracking>
<!-- other processors removed for brevity -->
<processor type="Sitecore.Analytics.Pipelines.StartTracking.ProcessItem, Sitecore.Analytics"/>
</startTracking>

This processor actually just runs another pipeline defined in Sitecore.Analytics.config, and the last processor in this pipeline is where things start to happen:


<processItem>
<!-- other processors removed for brevity -->
<processor type="Sitecore.Analytics.Pipelines.ProcessItem.ProcessProfiles, Sitecore.Analytics" />
</processItem>

When Sitecore items are associated with profiles the association is stored in the __Tracking field as xml.


<?xml version="1.0" encoding="UTF-8"?>
<tracking>
   <profile id="{ABCD566E-264B-4E73-8A67-970CFCCAA82A}" name="Sales Cycle" presets="evaluate|100||">
      <key name="Search and Inspiration" value="0" />
      <key name="Evaluate" value="10" />
      <key name="Enquire" value="0" />
   </profile>
</tracking>

The ProcessProfiles processor iterates over the TrackingField instances added to the pipeline args by previous processors and passes each TrackingField into the TrackingFieldProcessor class to read the xml, extract the profiles and scores associated with each profile key and store those values against the current contact, ready to be flushed into xDb at the end of the session.


public override void Process(ProcessItemArgs args)
{
    Assert.ArgumentNotNull(args, "args");
    
    foreach (TrackingField trackingParameter in args.TrackingParameters)
    {
        TrackingFieldProcessor.ProcessProfiles(args.Interaction, trackingParameter);
    }
}

Sitecore’s CollectParameters processor comes first in the processItem pipeline and simply adds the TrackingField of the context item to the list of tracking fields on the pipeline args.


public override void Process(ProcessItemArgs args)
{
    Assert.ArgumentNotNull(args, "args");

    if (!string.IsNullOrEmpty(args.Item["__Tracking"]))
    {
        Field item = args.Item.Fields["__Tracking"];

        if (item != null)
        {
            args.TrackingParameters.Add(new TrackingField(item));
        }
    }
    
    // source code removed for brevity
}

The following approach circumvents the content tagging process by mapping profiles to site taxonomies that can be evaluated inside the processItem pipeline.

Let’s use the example of leveraging existing content tagging for a site that sells cars. Each car on the site is already tagged as Luxury, Family, Economic or Utility. This is a classic example of existing taxonomies mapping 1 to 1 with profiles. 

Profile map items can be created in the content tree (a profile map repository) that provide fields that allow the setup of rules that say:

“where the context item (Page) is linked to the item X (Family) in the field Y (Vehicle Type), associate the profile Z (Family Cars) with the current user”

Profile Map Item

To allow the selection of a specific profile on a profile map item we simply need to add a field of type Profile Cards which under the hood uses a TrackingField, yep, the same field that Sitecore stores profile information in when tagging pages with profiles. This means we can also take advantage of the TrackingFieldProcessor class in a custom processor in the processItem pipeline.

To add our pipeline we need to patch it in after the CollectParameters processor, not instead of it so that traditional content tagging continues to work if needed.


<pipelines>
  <processItem>
    <processor 
      type="Sitecore.Feature.ProfileMapper.Analytics.Pipelines.ProcessItem.CollectMappedParameters, Sitecore.Feature.ProfileMapper" 
      patch:after="*[@type='Sitecore.Analytics.Pipelines.ProcessItem.CollectParameters,Sitecore.Analytics']" />
  </processItem>
</pipelines>

Then the magic happens here:


public class CollectMappedParameters : ProcessItemProcessor
{
    public override void Process(ProcessItemArgs args)
    {
        foreach (var profileMapSet in GetProfileMapsSets())
        {

            // source code removed for brevity

            foreach (var profileMap in profileMapSet)
            {
                var trackingField = new ProfileMap(profileMap).EvaluateProfileMap(args.Item);

                if (trackingField != null)
                {
                    args.TrackingParameters.Add(trackingField);

                    if (matchSingle)
                        break;
                }
            }
        }
    }

    public virtual IEnumerable<IGrouping<ID, Item>> GetProfileMapsSets()
    {
        var sets = new ProfileMapRepository().GetProfileMapSets();

        return sets; 
    }
}

To summarise the code above:

  • A collection of profile map sets (a set could be market, price ranges etc.) is retrieved from a profile map repository (which uses a custom index for performance)
  • Each profile map within each set is evaluated against the current context item (Page) and if it’s a match returns its tracking field (containing the matching profile)
  • The tracking field is added to the TrackingParameters collection on the pipeline args, which means it will be used by the ProcessProfiles processor and passed to the TrackingFieldProcessor described above

Et voila! Without having to tag any Sitecore items with profiles we are associating profiles with our site users based on their browsing behaviour and based on taxonomies that already exist in the site.

We can extend profile mapping concepts, for example range maps that say:

“where the value of the field X (Price) on the context item (Page) is between LowerBounds (70000) and UpperBounds (1000000), associate the profile Y (Luxury) with the current user”.

Or really anything that applies to the business domain you are working with and of course we can use Sitecore’s rules field to take advantage of the rules engine.

This approach still requires us to create the profiles, profile keys, profile cards and pattern cards ahead of time and for these types of 1 to 1 mapping scenarios each profile and pattern card is typically a 1 to 1 mapping with a profile key and a score of 10 for its respective key and 0 in all others. For example:

1 to 1 profile key to cards

Happy profiling!

Advertisements

3 thoughts on “Sitecore Sunday Pt 2: Programmatic Profile Association

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