Friday 21 February 2020

xDB Migration Wizard - Complex Facets and Facet Lists

The documentation for mapping custom facets using the xDB migration wizard is quite good for simple facets (ie. 1-1 string field mapping between Mongo and xConnect), however I've come across a few clients now who have needed something a bit more complex - generally in the form of a facet list.

I'm going to assume that you've been through the documentation in the link above and successfully mapped one or more simple facets.

I'm going to run through the scenario of migrating a Contact facet called CustomFacet which has a IElementCollection<CustomObject> called CustomObjects in 8.x Mongo to a Contact facet called CustomFacet which has a Dictionary<string, CustomObject> called CustomObjects in 9.x xConnect. CustomObject has 2 fields:
  • Id: int
  • Start: DateTime
  • Name: string

NOTE: Before you do anything, make sure you've built and deployed your custom model(s).  Verify that the json looks correct, and all your properties are appearing in your json file!
Our json file looks like:
{
  "Name": "My.CustomModel",
  "Version": "1.0",
  "References": [
    {
      "Name": "XConnect",
      "Version": "1.0"
    },
    {
      "Name": "Sitecore.XConnect.Collection.Model",
      "Version": "9.0"
    }
  ],
  "Types": {
    "My.CustomFacet": {
      "Type": "Facet",
      "BaseType": "Sitecore.XConnect.Facet",
      "ClrType": "My.CustomFacet, ModelSerializer, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
      "Properties": {
        "CustomObjects": {
          "Type": {
            "String": "My.CustomObject"
          }
        }
      }
    },
    "My.CustomObject": {
      "Type": "Complex",
      "ClrType": "My.CustomObject, ModelSerializer, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
      "Properties": {
        "Id": {
          "Type": "Int32"
        },
        "Start": {
          "Type": "DateTime"
        },
        "Name": {
          "Type": "String"
        }
      }
    }
  },
  "Facets": [
    {
      "Target": "Contact",
      "Name": "Custom",
      "Type": "My.CustomFacet"
    }
  ]
}

our json in Mongo looks like:
{
    "_id" : LUUID("a1b928fb-1ba9-824c-8fa7-2b5bc92b36c9"),
    "Identifiers" : {
        "Identifier" : "fb28b9a1-a91b-4c82-8fa7-2b5bc92b36c9"
    },
    "CustomFacet" : {
        "CustomObjects" : {
            "1" : {
                "Id" : 58,
                "Name" : "My First Object",
                "Date" : ISODate("2020-02-20T07:28:36.376Z")
            },
            "2" : {
                "Id" : 57,
                "Name" : "My Second Object",
                "Date" : ISODate("2020-02-18T07:28:36.376Z")
            },
            "3" : {
                "Id" : 102,
                "Name" : "My Third Object",
                "Date" : ISODate("2020-02-17T07:28:36.376Z")
            }
        }
    }
}

I'll be using the Addresses facet as a reference, as this is very similar.  We'll just need to edit one file:

[SupportedIds(new string[] {"{85C3A33B-AAD7-4982-98E5-D67729DDBCA8}"})]
  public class CustomEntriesFromXdbListValueAccessorConverter : BaseItemModelConverter
  {
    public CustomEntriesFromXdbListValueAccessorConverter(IItemModelRepository repository)
      : base(repository)
    {
    }

    protected override ConvertResult ConvertSupportedItem(
      ItemModel source)
    {
      DictionaryEntriesFromDocumentValueReaderConverter valueReaderConverter1 = new DictionaryEntriesFromDocumentValueReaderConverter(this.ItemModelRepository);
      valueReaderConverter1.IgnoreValidation = true;
      valueReaderConverter1.DictionaryFieldName = "CustomObjects";
      DictionaryEntriesFromDocumentValueReaderConverter valueReaderConverter2 = valueReaderConverter1;
      valueReaderConverter2.FieldsWithValuesForTheKeysToExclude = new string[0];
      ValueAccessor valueAccessor = new ValueAccessor();
      ConvertResult convertResult = valueReaderConverter2.Convert(source);
      if (convertResult.WasConverted)
        valueAccessor.ValueReader = convertResult.ConvertedValue;
      return this.PositiveResult((IValueAccessor) valueAccessor);
    }
  }

Specify data to read from MongoDB

Navigate to /sitecore/system/Data Exchange/xDB Data Migration 8x to 9/Data Access/Value Accessor Sets/Providers/MongoDB and create the following items and fields:
  • MongoDB
    • MongoDB Contact
      • CustomFacet on MongoDB Contact
        Field Name:CustomFacet
        Converter Type:Sitecore.DataExchange.Providers.MongoDB.Converters.DataAccess.ValueAccessors.DocumentFieldValueAccessorConverter, Sitecore.DataExchange.Providers.MongoDB
    • MongoDB Contact CustomObject List
      Template:/sitecore/templates/Data Exchange/Providers/MongoDB/Data Access/Value Accessor Sets/MongoDB Document Value Accessor Set
      Converter type:Sitecore.DataExchange.Converters.DataAccess.ValueAccessors.ChildBasedValueAccessorSetConverter, Sitecore.DataExchange
      • CustomObject Entries on MongoDB Contact
        Template:/sitecore/templates/Data Exchange/Tools/xDB Data Migration Tool/Data Access/Value Accessors/MongoDB Non Preferred Entries from xDB List Value Accessor
        Converter Type:My.CustomEntriesFromXdbListValueAccessorConverter, My
    • MongoDB CustomObject Entry
      Template:/sitecore/templates/Data Exchange/Providers/MongoDB/Data Access/Value Accessor Sets/MongoDB Document Value Accessor Set
      Converter type:Sitecore.DataExchange.Converters.DataAccess.ValueAccessors.ChildBasedValueAccessorSetConverter, Sitecore.DataExchange
      • Id
        Template:/sitecore/templates/Data Exchange/Tools/xDB Data Migration Tool/Data Access/Value Accessors/MongoDB Preferred Entry from xDB List Value Accessor
        Converter type:Sitecore.DataExchange.Providers.MongoDB.Converters.DataAccess.ValueAccessors.DocumentFieldValueAccessorConverter, Sitecore.DataExchange.Providers.MongoDB
      • Date
        Template:/sitecore/templates/Data Exchange/Tools/xDB Data Migration Tool/Data Access/Value Accessors/MongoDB Preferred Entry from xDB List Value Accessor
        Converter type:Sitecore.DataExchange.Providers.MongoDB.Converters.DataAccess.ValueAccessors.DocumentFieldValueAccessorConverter, Sitecore.DataExchange.Providers.MongoDB
      • Name
        Template:/sitecore/templates/Data Exchange/Tools/xDB Data Migration Tool/Data Access/Value Accessors/MongoDB Preferred Entry from xDB List Value Accessor
        Converter type:Sitecore.DataExchange.Providers.MongoDB.Converters.DataAccess.ValueAccessors.DocumentFieldValueAccessorConverter, Sitecore.DataExchange.Providers.MongoDB

Specify data to write to xConnect contact facet

Navigate to /sitecore/system/Data Exchange/xDB Data Migration 8x to 9/Data Access/Value Accessor Sets/Providers/xConnect
  • xConnect
    • xConnect Contact
      • CustomFacet on xConnect Contact
        Template:/sitecore/templates/Data Exchange/Providers/xConnect/Data Access/Value Accessors/xConnect Entity Facet Value Accessor
        Facet Definition:Collection Models/xDB Data Migration Tool/xDB Migratino Collection Model/Facets/Contact/CustomFacet
        Mapping Set:Value Mapping Sets/MongoDB to xConnect Contact Mappings/MongoDB CustomObjects to xConnect Contact CustomFacet
        Converter Type:Sitecore.DataExchange.Providers.XConnect.Converters.DataAccess.EntityFacetValueAccessorConverter, Sitecore.DataExchange.Providers.XConnect
    • xConnect Contact CustomFacet
      Template:/sitecore/templates/Data Exchange/Providers/xConnect/Data Access/Value Accessor Sets/xConnect Entity Facet Value Accessor Set
      Converter type:Sitecore.DataExchange.Converters.DataAccess.ValueAccessors.ChildBasedValueAccessorSetConverter, Sitecore.DataExchange
      • CustomObjects on CustomFacet on xConnect Contact
        Template:/sitecore/templates/Data Exchange/Providers/xConnect/Data Access/Value Accessors/xConnect Entity Facet Dictionary Property Value Accessor
        Facet Property:Collection Models/xDB Data Migration Tool/xDB Data Migration Collection Model/Facets/Contact/CustomFacet/CustomObjects
        Mapping set:Value Mapping Sets/MongoDB to xConnect Contact Mappings/MongoDB CustomObjects to xConnect Contact CustomObjects
        Converter Type:Sitecore.DataExchange.Providers.XConnect.Converters.DataAccess.EntityFacetDictionaryPropertyValueAccessorConverter, Sitecore.DataExchange.Providers.XConnect
    • xConnect Contact CustomObject
      Template:/sitecore/templates/Data Exchange/Providers/xConnect/Data Access/Value Accessor Sets/xConnect XObject Value Accessor Set
      Converter type:Sitecore.DataExchange.Converters.DataAccess.ValueAccessors.ChildBasedValueAccessorSetConverter, Sitecore.DataExchange
      • Id
        Template:/sitecore/templates/Data Exchange/Providers/xConnect/Data Access/Value Accessors/xConnect XObject Property Value Accessor
        Converter type:Sitecore.DataExchange.Providers.XConnect.Converters.DataAccess.XObjectPropertyValueAccessorConverter, Sitecore.DataExchange.Providers.XConnect
      • Date
        Template:/sitecore/templates/Data Exchange/Providers/xConnect/Data Access/Value Accessors/xConnect XObject Property Value Accessor
        Converter type:Sitecore.DataExchange.Providers.XConnect.Converters.DataAccess.XObjectPropertyValueAccessorConverter, Sitecore.DataExchange.Providers.XConnect
      • Name
        Template:/sitecore/templates/Data Exchange/Providers/xConnect/Data Access/Value Accessors/xConnect XObject Property Value Accessor
        Converter type:Sitecore.DataExchange.Providers.XConnect.Converters.DataAccess.XObjectPropertyValueAccessorConverter, Sitecore.DataExchange.Providers.XConnect

Configure mapping from MongoDB contact to xConnect contact facet

Navigate to /sitecore/system/Data Exchange/xDB Data Migration 8x to 9/Value Mapping Sets/MongoDB to xConnect Contact Mappings
  • MongoDB to xConnect Contact Mappings
    • MongoDB Contact to Contact Model
      • CustomFacet
        Template:/sitecore/templates/Data Exchange/Framework/Data Access/Mapping/Value Mapping
        Source Accessor:CustomFacet on MongoDB Contact
        Target Accessor:Data Access/Value Accessor Sets/Providers/xConnect/xConnect Contact/CustomFacet on xConnect Contact
        Converter type:Sitecore.DataExchange.Converters.DataAccess.Mappings.MappingConverter, Sitecore.DataExchange
    • MongoDB CustomObjects to xConnect Contact CustomFacet
      Template:/sitecore/templates/Data Exchange/Framework/Data Access/Mapping/Value Mapping Set
      Converter type:Sitecore.DataExchange.Converters.DataAccess.Mappings.ChildBasedMappingSetConverter, Sitecore.DataExchange
      • CustomObjects
        Template:/sitecore/templates/Data Exchange/Framework/Data Access/Mapping/Value Mapping
      • Source accessor:
        CustomObject Entries on MongoDB Contact
        Target aceessor:
        Data Access/Value Accessor Sets/Providers/xConnect/xConnect Contact CustomFacet/CustomObjects on CustomFacet on xConnect Contact

  • MongoDB CustomObject to xConnect Contact CustomObject
    Template:/sitecore/templates/Data Exchange/Framework/Data Access/Mapping/Value Mapping Set
    Converter type:Sitecore.DataExchange.Converters.DataAccess.Mappings.ChildBasedMappingSetConverter, Sitecore.DataExchange
    • Id
      Template:/sitecore/templates/Data Exchange/Framework/Data Access/Mapping/Value Mapping
      Source Accessor:Id on MongoDB Contact CustomObject
      Target Accessor:Data Access/Value Accessor Sets/Providers/xConnect/xConnect Contact CustomObject/Id
      Converter type:Sitecore.DataExchange.Converters.DataAccess.Mappings.MappingConverter, Sitecore.DataExchange
    • Name
      Template:/sitecore/templates/Data Exchange/Framework/Data Access/Mapping/Value Mapping
      Source Accessor:Id on MongoDB Contact CustomObject
      Target Accessor:Data Access/Value Accessor Sets/Providers/xConnect/xConnect Contact CustomObject/Id
      Converter type:Sitecore.DataExchange.Converters.DataAccess.Mappings.MappingConverter, Sitecore.DataExchange
    • Date
      Template:/sitecore/templates/Data Exchange/Framework/Data Access/Mapping/Value Mapping
      Source Accessor:Id on MongoDB Contact CustomObject
      Target Accessor:Data Access/Value Accessor Sets/Providers/xConnect/xConnect Contact CustomObject/Id
      Converter type:Sitecore.DataExchange.Converters.DataAccess.Mappings.MappingConverter, Sitecore.DataExchange