A colleague of mine has been looking at some custom page events and reporting in Sitecore 8.1 recently. One thing which came to light during this work is that some of the personalisation rule code in the product didn't appear to work as expected. In case anyone else is looking at this aspect of the software, he's a summary of the issue we were seeing, in the hope that it can help you avoid the time we spent looking into this:
The rule...
url copied!
With 8.1, one of the new features for analytics is the ability to write rules which can detect Page Events (or Goals) in either the current session or a previous session. The one we were looking at was:
This is supposed to allow you to trigger a personalisation when a certain page event has been recorded for the current user, and it's custom data matches the criteria specified. However we found that this did not work as expected.
The issue...
url copied!
When we set up test data for this rule in 8.1 Update 3 and tried it out, we noted that:
- If the Page Event is recorded in the current session then the rule ignores the custom data when deciding whether to trigger. It will trigger no matter what custom data is recorded.
- If the Page Event was recorded in a session from the past then the rule obeys the custom data when deciding whether to trigger.
Cue some head-scratching, and a bit of debugging...
The rule's logic is part of the
PageEventWithCustomDataWasTriggeredDuringPastOrCurrentInteractionCondition
class (Snappy name, huh?) in the Sitecore.Analytics.Rules.Conditions namespace. So we grabbed our trusty decompiler and looked through what this code was doing when a rule is evaluated. Through inheritance, this class makes use of the same
Execute()
method as the
PageEventWasTriggeredDuringPastOrCurrentInteractionCondition
class. That code, tidied up a bit, looks like:
protected override bool Execute(T ruleContext)
{
// -- snip
if (this.HasEventOccurredInInteraction((IInteractionData) Tracker.Current.Session.Interaction))
{
return true;
}
// -- snip
return this.FilterKeyBehaviorCacheEntries(Tracker.Current.Contact.GetKeyBehaviorCache()).Any<KeyBehaviorCacheEntry>((Func<KeyBehaviorCacheEntry, bool>) (entry =>
{
Guid id = entry.Id;
Guid? pageEventGuid = this.PageEventGuid;
if (pageEventGuid.HasValue)
{
return id == pageEventGuid.GetValueOrDefault();
}
return false;
}));
}
Based on that, if the event happened in the current session then only the
HasEventOccurredInInteraction()
method is called. If not, the
FilterKeyBehaviorCacheEntries()
method is called. Digging deeper
FilterKeyBehaviorCacheEntries()‘s behaviour depends on which rule is being run (it may or may not filter based on custom event data, depending on whether you're using the "with custom data" version of the rule or not).
But
HasEventOccurredInInteraction()
does not have any code that looks at the custom data, and it makes no reference to the
FilterKeyBehaviorCacheEntries()
method which does. It looks like this:
protected override bool HasEventOccurredInInteraction(IInteractionData interaction)
{
// -- snip
return ((IEnumerable<Page>) interaction.Pages).SelectMany<Page, PageEventData>((Func<Page, IEnumerable<PageEventData>>) (page => page.PageEvents)).Any<PageEventData>((Func<PageEventData, bool>) (pageEvent =>
{
if (pageEvent.IsGoal)
{
return false;
}
Guid eventDefinitionId = pageEvent.PageEventDefinitionId;
Guid? pageEventGuid = this.PageEventGuid;
if (pageEventGuid.HasValue)
{
return eventDefinitionId == pageEventGuid.GetValueOrDefault();
}
return false;
}));
}
That code seems to explain the behaviour we've been seeing – the logic is actually different depending on whether the Page Event fired in the current session or not...