List Manager: Segmented List From Custom Rules Using Custom Facet Fields

Versions

Sitecore 8 Update 2

EXM 3 rev 150223

Tasks

Create a custom facet with fields on a contact

Segment a List Manager Segmented List with custom rules using the custom facet fields

Details

In Sitecore, using the List Manager, you can create Segmented Lists segmented with rules.  These segmented lists can be used with EXM (formerly known as ECM) to send email messages.

Sitecore List Manager Segmented List builder with rules

SegmentedListRules

We’ll start by creating a custom facet with fields with a custom program to import a recipient list and create or update contacts for the list as described here.

Create custom facet field

Adam Conn describes how to add the custom contact facet and fields

http://www.sitecore.net/learn/blogs/technical-blogs/getting-to-know-sitecore/posts/2014/09/introducing-contact-facets.aspx

Here’s a code snippet to add the facet with fields we’ll use for segmentation rules


// Custom Facet fields:  contactType, salutation, age
var eXMContactFacetNew = newContact.GetFacet<IEXMContact>(EXMContactConstants.FACET_NAME);
eXMContactFacetNew.ContactType = "Employer";
eXMContactFacetNew.Salutation = "Ms.";
eXMContactFacetNew.Age = 22;

Facet Type

    public interface IEXMContact : IFacet
    {
        string ContactType { get; set; }
        string Salutation { get; set; }
        int Age { get; set; }
    }

Facet in Mongo Db Contact

FacetInMongo

To use the custom facet fields in a rule action, we need to index the fields.  Here is the configuration and computed index code to do that:

/App_config/include/EXMCustomContactData.config

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <model>
      <elements>
        <element interface="LaunchSitecore.Models.EXM.IEXMContact, LaunchSitecore" implementation="LaunchSitecore.Models.EXM.EXMContact, LaunchSitecore" />
      </elements>
      <entities>
        <contact>
          <facets>
            <facet name="EXMContact" contract="LaunchSitecore.Models.EXM.IEXMContact, LaunchSitecore" />
          </facets>
        </contact>
      </entities>
    </model>

    <!--Custom index field definition-->
    <contentSearch>
      <configuration type="Sitecore.ContentSearch.ContentSearchConfiguration, Sitecore.ContentSearch">
        <indexes hint="list:AddIndex">
          <index id="sitecore_analytics_index" type="Sitecore.ContentSearch.LuceneProvider.LuceneIndex, Sitecore.ContentSearch.LuceneProvider">
            <param desc="name">$(id)</param>
            <param desc="folder">$(id)</param>
            <param desc="propertyStore" ref="contentSearch/indexConfigurations/databasePropertyStore" param1="$(id)" />
            <configuration ref="contentSearch/indexConfigurations/defaultLuceneIndexConfiguration">
              <fieldMap ref="contentSearch/indexConfigurations/defaultLuceneIndexConfiguration/fieldMap">
                <fieldNames hint="raw:AddFieldByFieldName">
                  <field fieldName="contact.EXMContact.Age" type="System.Int32"  storageType="YES" indexType="TOKENIZED" vectorType="WITH_POSITIONS_OFFSETS" boost="1f" emptyString="_EMPTY_" nullValue="_NULL_" settingType="Sitecore.ContentSearch.LuceneProvider.LuceneSearchFieldConfiguration, Sitecore.ContentSearch.LuceneProvider" />
                  <field fieldName="contact.EXMContact.ContactType" type="System.String" storageType="YES" indexType="TOKENIZED" vectorType="WITH_POSITIONS_OFFSETS" boost="1f" emptyString="_EMPTY_" nullValue="_NULL_" settingType="Sitecore.ContentSearch.LuceneProvider.LuceneSearchFieldConfiguration, Sitecore.ContentSearch.LuceneProvider" />
                  <field fieldName="contact.EXMContact.Salutation" type="System.String" storageType="YES" indexType="TOKENIZED" vectorType="WITH_POSITIONS_OFFSETS" boost="1f" emptyString="_EMPTY_" nullValue="_NULL_" settingType="Sitecore.ContentSearch.LuceneProvider.LuceneSearchFieldConfiguration, Sitecore.ContentSearch.LuceneProvider" />
                </fieldNames>
              </fieldMap>
              <fields hint="raw:AddComputedIndexField">
                <field fieldName="contact.EXMContact.Age" type="LaunchSitecore.Models.EXM.Indexing.EXMContact_Fields_Indexing_Age, LaunchSitecore" matchField="type" matchValue="contact"/>
                <field fieldName="contact.EXMContact.ContactType" type="LaunchSitecore.Models.EXM.Indexing.EXMContact_Fields_Indexing_ContactType, LaunchSitecore" matchField="type" matchValue="contact"/>
                <field fieldName="contact.EXMContact.Salutation" type="LaunchSitecore.Models.EXM.Indexing.EXMContact_Fields_Indexing_Salutation, LaunchSitecore" matchField="type" matchValue="contact"/>
              </fields>
            </configuration>
          </index>
        </indexes>
      </configuration>
    </contentSearch>
    <!--End of custom index field definition-->
  </sitecore>
</configuration>

This includes an example of an integer (“Age”) and strings (“ContactType,”Salutation”).

The computed index code example from “Age” with “ContactType” and “Salutation” similar:

public class EXMContact_Fields_Indexing_Age : IComputedIndexField
    {
        public object ComputeFieldValue(IIndexable indexable)
        {
            var contactIndexable = indexable as ContactIndexable;
            if (contactIndexable != null)
            {
                ContactRepositoryBase contactRepositoryBase = Factory.CreateObject("contactRepository", true) as ContactRepositoryBase;
                if (contactRepositoryBase != null)
                {
                    var contact = contactRepositoryBase.LoadContactReadOnly((Guid)contactIndexable.Id.Value);
                    var contactData = contact.GetFacet<IEXMContact>(EXMContactConstants.FACET_NAME);
                    return contactData.Age;
                }
            }
            return null;
        }

        public string FieldName { get; set; }

        public string ReturnType { get; set; }
    }

The segmented rule is added to /sitecore/system/Settings/Rules/Definitions/Elements/Segment BuilderSegmentRule

Age

Text

where the contact age [operatorid,Operator,,compares to] [Age,PositiveInteger,defaultValue=&validationText=Please enter a valid age.,age]

Type

LaunchSitecore.Extensions.EXM.Rules.ContactAgeCondition, LaunchSitecore

(your type)

Contact Type

Text

where the contact type [operatorid,StringOperator,,compares to] [value,,,specific contacttype]

Type

 

Here’s the code for the rules demonstrating the integer Age and string Contact Type

     public class ContactAgeCondition<T> : TypedQueryableOperatorCondition<T, IndexedContact> where T : VisitorRuleContext<IndexedContact>
    {
        public ContactAgeCondition()
        {
            this.Age = Int32.MinValue;
        }
        protected override Expression<Func<IndexedContact, bool>> GetResultPredicate(T ruleContext)
        {
            if (this.Age != Int32.MinValue)
            {
                // This is a GEM of a snippet to get the integer value
                return base.GetCompareExpression<int>(c => (int)c[(ObjectIndexerKey)"contact.exmpbscontact.age"], this.Age);
            }
            return c => false;
        }

        //Properties
        public int Age { get; set; }
    }

    public class ContactTypeCondition<T> : TypedQueryableStringOperatorCondition<T, IndexedContact> where T: VisitorRuleContext<IndexedContact>
    {
        protected override Expression<Func<IndexedContact, bool>> GetResultPredicate(T ruleContext)
        {
             return base.GetCompareExpression(c => c["contact.exmpbscontact.contacttype"], base.Value);
        }
    }

And that’s all you need to do to use the new rules to segment a list by custom facet fields in a contact.

2 thoughts on “List Manager: Segmented List From Custom Rules Using Custom Facet Fields

Add yours

  1. has anyone successfully added a date field to segment on? I’m having issues with Sitecore tinkering with the date before storing it into xDB and later on it’s stored as DateTime.Min value in Lucene..

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 )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Blog at WordPress.com.

Up ↑

%d bloggers like this: