How to Setup Complex Mapping Rules

Migrating data from a Salesforce instance to an unrelated Salesforce instance the mapping between system tables can be complex and mapping rules are needed to control how records in a CopyStorm backup map to records in a target Salesforce.

This article review two types of mapping rules:

  • Simple column value mapping rules.
  • Complex mapping rules.

The Basics

All mapping rules provide a map of Ids in the CopyStorm backup to corresponding Ids in the target Salesforce.

For example:

  • If a User record in CopyStorm has the same email address as a record in the target Salesforce, map the CopyStorm User.Id to the Salesforce User.Id.

Mapping rules are stored in a configuration file named “DefaultRecordMapRule.xml”, stored in any of the normal places for CopyStorm/Restore configuration files. It contains two types of supported entries:

  • ColumnBasedBuilder — a mapping rule determining a match between two records based on the value of one or more columns.
  • FunctionBasedBuilder — a mapping rule determining a match between two records based on user supplied Java code, using any technique to determine if records match.

The build-in DefaultRecordMapRule.xml looks like:


    <!-- Column Based Builders match records based just on the values of one or more columns. -->
    <ColumnBasedBuilder tableName="User" columns="Email" /> 
    <ColumnBasedBuilder tableName="Profile" columns="Name" /> 
    <ColumnBasedBuilder tableName="UserRole" columns="Name" /> 
    <ColumnBasedBuilder tableName="Group" columns="Name" /> 
    <ColumnBasedBuilder tableName="RecordType" columns="Name,SObjectType" />

    <!-- Function based builders require custom code to build a mapping. -->
    <FunctionBasedBuilder tableName="Pricebook2" class="Pricebook2RecordMapRuleBuilder" /> 
    <FunctionBasedBuilder tableName="Organization" class="OrganizationRecordMapRuleBuilder" />


Simple Column Value Mapping Rules

A Simple Column Value Mapping Rule is used when a CopyStorm and Salesforce record should be considered the same if one or more columns in the two records match. For example, the following rule indicates that two User records are the same if the records contain the same email address:

    <ColumnBasedBuilder tableName="User" columns="Email" />

A rule indicating that Record Types match if they are for the same SObject and have the same Name is:

    <ColumnBasedBuilder tableName="RecordType" columns="Name,SObjectType" />

Function Based Rule

A Function Based Rule uses arbitrary Java code to build an Id mapping between CopyStorm records and Salesforce records. For example:

  • The built-in Pricebook2 function rule ensures that the standard pricebook in the CopyStorm backup maps to the standard pricebook in the target Salesforce.
  • The built-in Organization function rule ensures that the Salesforce Org Id in the CopyStorm backup maps to the Org Id of the target Salesforce.

There are two steps required to add a new Function Based Rule to CopyStorm/Restore:

  1. Write a Java class implementing the rule and store the implementation in a jar within the “lib” sub-directory of the CopyStorm/Restore installation.
  2. Add a “FunctionBasedBuilder” XML entry to the “DefaultRecordMapRuleBuilder.xml” file.

For example, this is the function based rule for Organization built into CopyStorm/Restore:

public class OrganizationRecordMapRuleBuilder extends AbstractDefaultRecordMapBuilder {

    private static String ORGANIZATION = "Organization";

    * Build a list of rules that map from CopyStorm record ids to ids in the target Salesforce.
    * @param session -- connection to the target Salesforce.
    * @param dataSoruce -- connection to the source CopyStorm database.
    * @returns list of rules mapping CopyStorm ids to Salesforce Ids
    @Override public List buildRules(Session session, DataSource dataSource) throws Exception {
        CopyForceTableFactory copyForceTableFactory = CopyForceTableFactory.getInstance(); 
        List results = new ArrayList(); 

        // If the CopyStorm backup does not include the Organization table then we have no way of determining the original organization id.
        if( !copyForceTableFactory.isTableDefined(dataSource, ORGANIZATION )) { return new ArrayList(); } 

        JdbcTemplate jdbcTemplate = new JdbcTemplate( dataSource ); 
        ICopyForceTable csTable = copyForceTableFactory.getTable(dataSource, ORGANIZATION ); 
        List csOrgs = jdbcTemplate.queryForList( "SELECT Id FROM " + csTable.getCopyForceName()  , String.class ); 

        if( 1 != csOrgs.size()) { return results; } 

        RecordMapRule rule = new RecordMapRule(ORGANIZATION, "Id"); 
        rule.getValueMap().put(csOrgs.get(0), session.getUserInfo().getOrganizationId()); 
        return results;