Create a calculated facet
This topic demonstrates how to implemented a calculated facet named AverageSpend
. The facet stores data about the contact’s purchase history, including average spend per purchase.
Create and register a facet
-
Create a facet class:
RequestResponseusing System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Documentation { [FacetKey(MyCollectionModel.FacetKeys.AverageSpend)] public class AverageSpend : Sitecore.XConnect.Facet { public const string DefaultFacetKey = MyCollectionModel.FacetKeys.AverageSpend; public int TotalPurchases { get; set; } public decimal TotalSpend { get; set; } // Assumes single currency public decimal AverageSpendPerPurchase { get; set; } } }
NoteThis example assumes that all purchases are made using the same currency.
-
Register the facet in a model:
RequestResponsemodelBuilder.DefineFacet<Contact, AverageSpend>(AverageSpend.DefaultFacetKey);
Implement a calculated facet plugin
-
Create a calculated facet handler that implements
Sitecore.XConnect.Service.MergingCalculatedFacetHandler<AverageSpend>
. Notice the following:-
An
InteractionFacetDependency
object is passed into the base handler. This ensures that theWebVisit
facet is loaded if it is present on the interaction. However, the facet is not required for the handler to run. -
The
Merge()
method determines what happens to this facet when two contacts are merged. In this, the total spend and total purchase are added together, and the average spend per purchase is recalculated. You must implement theMerge()
method for calculated facets. -
The
UpdateFacet()
method determines what happens when a new interaction is submitted to xConnect. -
All methods return
true
if the facet was updated.
RequestResponseusing Sitecore.XConnect; using Sitecore.XConnect.Collection.Model; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Documentation { public class AverageSpendHandler : Sitecore.XConnect.Service.MergingCalculatedFacetHandler<AverageSpend> { public AverageSpendHandler() : base(AverageSpend.DefaultFacetKey, new[] { new Sitecore.XConnect.Service.InteractionFacetDependency(WebVisit.DefaultFacetKey, false) }) { } protected override bool Merge(AverageSpend source, AverageSpend target) { if (source.TotalSpend > 0) { target.TotalSpend += source.TotalSpend; target.TotalPurchases += source.TotalPurchases; target.AverageSpendPerPurchase = target.TotalSpend / target.TotalPurchases; return true; } return false; } protected override bool UpdateFacet(AverageSpend currentFacet, Sitecore.XConnect.Interaction interaction) { bool edited = false; var referrer = interaction.WebVisit() != null ? interaction.WebVisit().Referrer : String.Empty; if (!String.IsNullOrEmpty(referrer)) { currentFacet.LatestReferrer = referrer; edited = true; } var outcomes = interaction.Events.OfType<Outcome>(); // Get all purchase outcomes in interaction if (outcomes != null && outcomes.Any()) { foreach (var purchase in outcomes) { currentFacet.TotalPurchases++; // If there are purchases, increment purchase count currentFacet.TotalSpend += purchase.MonetaryValue; } // Set average spend per purchase (NOT spend per visit) currentFacet.AverageSpendPerPurchase = currentFacet.TotalSpend / currentFacet.TotalPurchases; } return edited; } }
NoteIf the the
Required
property of anyInteractionFacetDependency
is set totrue
, the handler will only execute if this facet is present on the interaction. -
-
Open the
\App_data\config\sitecore\Collection\sc.XConnect.Collection.Model.Plugins.xml
configuration file in xConnect. -
Add two entries for the handler - one for
ICalculatedFacetHandler
and one forIContactMergeHandler
:RequestResponse<ICalculatedFacetHandler.AverageSpendHandler> <Type>Documentation.AverageSpendHandler, Documentation</Type> <As>Sitecore.XConnect.Service.ICalculatedFacetHandler, Sitecore.XConnect.Service</As> <LifeTime>Singleton</LifeTime> </ICalculatedFacetHandler.AverageSpendHandler> <IContactMergeHandler.AverageSpendHandler> <Type>Documentation.AverageSpendHandler, Documentation</Type> <As>Sitecore.XConnect.Service.IContactMergeHandler, Sitecore.XConnect.Service</As> <LifeTime>Singleton</LifeTime> </IContactMergeHandler.AverageSpendHandler>
Pass in configuration options
You can pass configuration options into a calculated facet handler. In the following example, a configuration option controls whether or not the LatestReferrer
property of the AverageSpend
facet is set.
-
Add a public property to represent your option - for example,
public bool PopulateReferrer { get; }
. -
Add a constructor that takes an
IConfiguration
object. The following scenario assumes that the configuration option is namedPopulateReferrer
:RequestResponseusing Microsoft.Extensions.Configuration; using Sitecore.XConnect; using Sitecore.XConnect.Collection.Model; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Documentation { public class AverageSpendHandler : Sitecore.XConnect.Service.MergingCalculatedFacetHandler<AverageSpend> { public AverageSpendHandler(IConfiguration options) : this(options.GetValue<bool>(nameof(PopulateReferrer))) { } public AverageSpendHandler(bool populateReferrer) : base(AverageSpend.DefaultFacetKey, new[] { new Sitecore.XConnect.Service.InteractionFacetDependency(WebVisit.DefaultFacetKey, false) }) { PopulateReferrer = populateReferrer; } public bool PopulateReferrer { get; } protected override bool Merge(AverageSpend source, AverageSpend target) { bool edited = false; if (PopulateReferrer) { if (String.IsNullOrEmpty(target.LatestReferrer)) { target.LatestReferrer = source.LatestReferrer; edited = true; } } if (source.TotalSpend > 0) { target.TotalSpend += source.TotalSpend; target.TotalPurchases += source.TotalPurchases; target.AverageSpendPerPurchase = target.TotalSpend / target.TotalPurchases; return edited; } return false; } protected override bool UpdateFacet(AverageSpend currentFacet, Sitecore.XConnect.Interaction interaction) { bool edited = false; var referrer = interaction.WebVisit() != null ? interaction.WebVisit().Referrer : String.Empty; if (PopulateReferrer && !String.IsNullOrEmpty(referrer)) { currentFacet.LatestReferrer = referrer; edited = true; } var outcomes = interaction.Events.OfType<Outcome>(); // Get all purchase outcomes in interaction if (outcomes != null && outcomes.Any()) { foreach (var purchase in outcomes) { currentFacet.TotalPurchases++; // If there are purchases, increment purchase count currentFacet.TotalSpend += purchase.MonetaryValue; } // Set average spend per purchase (NOT spend per visit) currentFacet.AverageSpendPerPurchase = currentFacet.TotalSpend / currentFacet.TotalPurchases; } return edited; } } }
-
Open
\App_data\config\sitecore\Collection\sc.XConnect.Collection.Model.Plugins.xml
and add options into configuration as shown. If the option is used during merge and update, specify the option twice as shown.RequestResponse<ICalculatedFacetHandler.AverageSpendHandler> <Type>Documentation.AverageSpendHandler, Documentation</Type> <As>Sitecore.XConnect.Service.ICalculatedFacetHandler, Sitecore.XConnect.Service</As> <LifeTime>Singleton</LifeTime> <Options> <PopulateReferrer>true</PopulateReferrer> </Options> </ICalculatedFacetHandler.AverageSpendHandler> <IContactMergeHandler.AverageSpendHandler> <Type>Documentation.AverageSpendHandler, Documentation</Type> <As>Sitecore.XConnect.Service.IContactMergeHandler, Sitecore.XConnect.Service</As> <LifeTime>Singleton</LifeTime> <Options> <PopulateReferrer>true</PopulateReferrer> </Options> </IContactMergeHandler.AverageSpendHandler>
Get calculated facets
Use the xConnect Client API to retrieve calculated facets in the same way as any other facet. Do not edit calculated facets directly.