Hacking Sitecore and Life one pipeline at a time!

Creating Custom Sitecore xDB Contact Facets

unlocking xdb

Blog Series: Unlocking xDB – Vol: #1  
Referenced Sitecore Version: Sitecore 8.1U3 – 8.2

Creating Custom Sitecore xDB Contact Facets in Sitecore 8.1U3 and Sitecore 8.2

Over the last couple of days, I’ve enjoyed the challenge of trying to create a custom Sitecore xDB Contact Facet.  If you’re unsure what a Facet is, don’t worry.  There are so many meanings that it can get confusing.  Just know that a Facet in this context is a collection of data that relates in some way.

For example, say you are a product company and you want to collect additional information about customers as they browse the site. In this fictitious example, you want to track a custom and unique Customer Number as well as a Segment property for each of your customers. These two pieces of information are related into a collection called “Marketing Data”. — This is what we mean by a Sitecore xDB Facet.

There are a couple different blog posts floating out in the wild that have been written over the years.  This one and this blog post by Adam Conn are the two that are often linked. The first one has some serious errors that prevented this from working for me.  Adam’s blog post is a little bit better, but was written two years ago and slightly off.  So, this is my attempt to demystify how this really works, and confirm the appropriate path forward.

Out of the Box Facets

Before we talk about custom facets, let me go over some of the OOTB facets that ship with Sitecore 8. By the way, a lot of the examples in this series will showcase images of the raw MongoDB collection. I highly recommend that you buy, download, and install RoboMongo in order to browse the xDB Analytics table.

A peek into the Sitecore configuration, you’ll see that we have the following entities configuration node that lives in the /sitecore/model node:

<entities>
<contact>
<factory type="Sitecore.Analytics.Data.ContactFactory, Sitecore.Analytics" singleInstance="true"/>
<template type="Sitecore.Analytics.Data.ContactTemplateFactory, Sitecore.Analytics" singleInstance="true"/>
<facets>
<facet name="Personal" contract="Sitecore.Analytics.Model.Entities.IContactPersonalInfo, Sitecore.Analytics.Model"/>
<facet name="Addresses" contract="Sitecore.Analytics.Model.Entities.IContactAddresses, Sitecore.Analytics.Model"/>
<facet name="Emails" contract="Sitecore.Analytics.Model.Entities.IContactEmailAddresses, Sitecore.Analytics.Model"/>
<facet name="Phone Numbers" contract="Sitecore.Analytics.Model.Entities.IContactPhoneNumbers, Sitecore.Analytics.Model"/>
<facet name="Picture" contract="Sitecore.Analytics.Model.Entities.IContactPicture, Sitecore.Analytics.Model"/>
<facet name="Communication Profile" contract="Sitecore.Analytics.Model.Entities.IContactCommunicationProfile, Sitecore.Analytics.Model"/>
<facet name="Preferences" contract="Sitecore.Analytics.Model.Entities.IContactPreferences, Sitecore.Analytics.Model"/>
<facet name="TestCombinations" contract="Sitecore.ContentTesting.Model.Entities.ITestCombinationsData, Sitecore.ContentTesting.Model" patch:source="Sitecore.ContentTesting.config"/>
<facet name="SocialProfile" contract="Sitecore.Social.Connector.Facets.Contact.SocialProfile.ISocialProfileFacet, Sitecore.Social" patch:source="Sitecore.Social.Analytics.Model.config"/>
</facets>
</contact>
</entities>
Here you can see we have Personal, Addresses, Emails, etc.  These are the standard facets shipped with Sitecore 8. Values are only stored in xDB if there is a value. Otherwise, nothing will be saved in the mongodb. Here’s a screenshot of what an example of these will look like in mongodb.
xdb facets

 Facet Composition

There are three parts to a Facet:

  • Facet
    • The facet itself.  This is set of information of a similar subject matter.
    • In the example above, Personal and Emails are examples of a Facet.
    • The Facet itself is nothing more a marker (identified by IFacet) indicating the root of the Element tree. This means that a Facet is also an Element, done through Inheritance.
    • The Facet defines the root Element construct by way of referencing an interface to an Element.
  • Element
    • This is the actual data structure defining a collection of data.
    • There are two pieces to an Element:
      • Interface Type
      • Implementation Type
    • The class structure of an element can reference other Elements as properties, but also as a Dictionary Elements, which is a collection of Elements.
  • Attribute
    • An attribute is a key value field that contains the actual data.
    • Examples above include FirstNameSurname, and SmtpAddress are attributes.

Adding Custom Facets

Alright, enough introductory information about facets, let’s get to the TL;DR of why you are probably still reading!  The following step by step will demonstrate how to create a custom facet in your solution.

Step 1: Gather information about the facet

Create a custom facet is actually not that difficult.  What’s more difficult, IMHO, is obtaining what kind of information you want in your facet? Getting the following stuff together before you start coding will allow you not break your coding groove.

  1. The name of the Facet.
  2. What information do you want to capture?
    1. Name of field
    2. Type of field (strings, GUID, integrers, and floats.

Step 2: Create the element contract that defines the facet

By creating a facet, what we’re really doing is creating the root element interface that will contain all of the other structures going into our facet. We will define our interface inheriting from IFacet.

using Sitecore.Analytics.Model.Framework;

namespace SitecoreHacker.Sandbox.Facets
{
public interface IMarketingData : IFacet
{
string CustomerId { get; set; }
string Segment { get; set; }
}
}

Here I have defined an interface called IMarketingData that is based on the IFacet interface. There are two things I want to capture in this IFacet: CustomerId and Segment. Both of these values are strings.

That was easy, right?

Step 3: Create the implementation of your element contract

Now I want to create the concrete class that can implement the interface that we just created.

using System;
using Sitecore.Analytics.Model.Framework;

namespace SitecoreHacker.Sandbox.Facets
{
    [Serializable]
    public class MarketingData: Facet, IMarketingData
    {

        private const string CUSTOMER_ID = "CustomerId";
        private const string SEGEMENT = "Segment";

        #region Properties
        public string CustomerId
        {
            get { return GetAttribute<string>(CUSTOMER_ID); }
            set { SetAttribute(CUSTOMER_ID, value); }
        }

        public string Segment
        {
            get { return GetAttribute<string>(SEGEMENT); }
            set { SetAttribute(SEGEMENT, value); }
        }
        #endregion

        public MarketingData()
        {
            EnsureAttribute<string>(CUSTOMER_ID);
            EnsureAttribute<string>(SEGEMENT);
        }
    }
}

Alright, there’s a little bit more going on here so let’s step through it.

  1. First, just create a class and base it off of Facet and IMarketingData
    1. We base off of Facet because this is our root Element.  If we were just creating an  Element that would be used in this Facet, and in addition to the Facet Element, then we would have a base class of Element More on that later.
  2. Add the [Serializable] attribute.
    1. This would seem unimportant, but becomes super important depending on the type of Session State being run.  So just use it. Don’t ask questions. =)
  3. Now the implementation class has three parts as follows.  Don’t implement any other functions in this class outside of the following:
    1. Constants
      1. For every attribute that you have, define a constant that will provide a label for that attribute.
      2. Protip: I found out the hard way that the constants defined, the value must equal the actual name of the class property for the same attribute.
    2. Class Properties
      1. For every attribute, create getters and setters.
      2. Important: Name the Property the same as the value of the constant that you set for it.
      3. For Getters:
        1. return GetAttribute<type>(string name);
        2. Type of your attribute: string, Guid, Integers, or Float
        3. name: Reference the constant variable defined for your attribute.
      4. For Setters
        1. SetAttribute<type>(string name, value);
        2. Type of your attribute: string, Guid, Integers, or Float
        3. name: Reference the constant variable defined for your attribute.
    3. Class Constructor
      1. No parameters
      2. Call EnsureAttribute<string>(string name); for each attribute.
  4. Save and compile!

 Step 4: Add Custom Facet to Configuration

Now that our C# objects are created, saved, and compiled, it’s time to configure Sitecore so that it knows about our custom facet. I recommend placing this config file in App_Config\Include\Y.CustomPatchFiles.

For the facet above, here’s what our patch file will look like:

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
	<sitecore>
    <model>
      <elements>
        <element interface="SitecoreHacker.Sandbox.Facets.IMarketingData, SitecoreHacker.Sandbox"
                 implementation="SitecoreHacker.Sandbox.Facets.MarketingData, SitecoreHacker.Sandbox"/>
      </elements>
      <entities>
        <contact>
          <facets>
            <facet name="Marketing Data" contract="SitecoreHacker.Sandbox.Facets.IMarketingData, SitecoreHacker.Sandbox"/>
          </facets>
        </contact>
      </entities>
    </model>
  </sitecore>
</configuration>

There are two parts to this config file, so let’s walk through them — backwards:

  1. In the /sitecore/model/entities/contact/facets node, we define a new <facet> node for our Facet
    1. The name attribute of the element is very important and is used as a string in code to reference the Facet.
    2. The contract attribute is the type of the interface that we created for the Facet, and that derives from IFacet
  2. In the /sitecore/model/elements node, we define a new <element> node for our Element.
    1. Now hold the phone, I thought we created a facet? Well, we did!  But a Facet is also an Element. It’s just a special Element. (Dig under the covers and you’ll find that the Facet class derives from the Element class.
      1. sitecore facet
    2. The interface attribute defines the type of our Interface.
    3. The implementation attribute defines the type of our concrete class.
  3. Save your configuration  patch file and deploy it and the compiled binaries to your Sitecore web instance.
  4. Validate Your configuration by checking /sitecore/admin/showconfig.aspx

Step 5: …

Step 6: Profit!  Well, Sort of.

So, our facet is now created and activated in Sitecore’s Experience Profile.  Any contacts created or fetched will be able to access this facet through the API.  But. until you do, nothing really saves in the xDB.

Reading and Writing to the Facet

In Adam Conn’s blog post, even though it’s written for Sitecore 7.5 and has slight differences regarding how to create a Facet that we just walked through, his blog post describing how to read and write data to the xDB Facets is spot on and worth a read.

Unlocking xDB Continues

In the next installment of Unlocking xDB, we’ll talk about how to use the custom facet that we’ve just created, and modify List Manager to allow us to upload a custom CSV file of Contact information and map data from the CSV file to our custom facet.

 

Tagged as: ,

Categorised in: Sitecore, Technology, Uncategorized

3 Responses »

  1. Awesome blog post Pete! Precise examples, step-by-step, very useful. Only … what happened to Step 5? 🙂
    I’m creating my first custom facet first thing tomorrow morning!

    Like

Trackbacks

  1. Loading xDB Contacts with CSV and Custom Fields | Sitecore Hacker
  2. No Speak Experience Profile Tab | Bending Sitecore

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Enter your email address to follow this blog and receive notifications of new posts by email.

Join 1,237 other subscribers

Blog Stats

  • 138,855 hits
Follow Sitecore Hacker on WordPress.com
Sitecore® and Own the Experience® are registered trademarks of Sitecore Corporation A/S in the U.S. and other countries.  This website is independent of Sitecore Corporation, and is not affiliated with or sponsored by Sitecore Corporation.