6

We have a Sitecore 8.1-3 instance. Previously we had used the user's Sitecore username as the identifier in xDB. Then after the site was live for some time we realized that using the user's email address was a much better solution. So we changed the site to use email address in the Identify method. And the site has been running for a while with that.

However this caused the unfortunate situation where we have some users that have two contacts in xDB - one identified by their Sitecore username and one identified by their email address.

Is there any way that I can write a script that would merge these? Ultimately what I would like to do is look for all of the contacts that are identified by their Sitecore username (OLD contact), then for each one of those go find their contact that is identified by their email address (NEW contact). I would like to then transfer all of the Sitecore analytics activity from the OLD contact to the NEW contact. Then delete the OLD contact. Is this even possible?

Right now the problem we have is that when you search in Experience Profile you get double results for some contacts. And some of the activity is assigned to one contact and some is assigned to the other contact. We would like to find a way to clean this up. Any ideas?

1
  • Have you tried what I've outlined in my answer? Did it work for you? Jan 31, 2017 at 14:21

1 Answer 1

4

Here's how I would approach this.

1) Execute this query in your MongoDB shell (or in Robomongo):

var ids = [];

db.Identifiers.aggregate(
    { $match: { _id: { $not: /@/ } } },
    { $project: { _id: 1 } }
).result.forEach(function(r) { ids.push(r._id) });

printjson(ids);

You'll get an array of all identifiers that aren't emails (i.e. don't contain the @ symbol).

2) Create a C# script (e.g. an .aspx page) and insert the ids you retrieved earlier:

string[] ids = { "Identifier1", "Identifier2", ... };

3) For every identifier, load the contact and merge it with the corresponding email-identified contact.

Note that in the code below I am using the ContactRepository.MergeContacts() API that was designed for merging two contacts into one.

var contactRepository = (ContactRepositoryBase)Factory.CreateObject("contactRepository", true);
LeaseOwner leaseOwner = new LeaseOwner("SOME_UNIQUE_WORKER_NAME", LeaseOwnerType.OutOfRequestWorker);

foreach (string identifier in ids)
{
    LockAttemptResult<Contact> lockResultOld = contactRepository.TryLoadContact(identifier, leaseOwner, TimeSpan.FromMinutes(1));

    if (lockResultOld.Status == LockAttemptStatus.Success)
    {
        Contact oldContact = lockResultOld.Object;

        try
        {
            // Get the new email ID from somewhere, e.g. from a facet.
            var emails = oldContact.GetFacet<IContactEmailAddresses>("Emails");
            string newIdentifier = emails.Entries[emails.Preferred].SmtpAddress;

            LockAttemptResult<Contact> lockResultNew = contactRepository.TryLoadContact(newIdentifier, leaseOwner, TimeSpan.FromMinutes(1));

            if (lockResultNew.Status == LockAttemptStatus.Success)
            {
                Contact newContact = lockResultNew.Object;

                try
                {
                    // This will merge all contact data, including all the visits, into the new contact.
                    contactRepository.MergeContacts(newContact, oldContact);

                    contactRepository.ReleaseContact(oldContact.ContactId, leaseOwner);

                    // When you save the new contact, the old contact will be marked as "obsoleted" in the database.
                    var options = new ContactSaveOptions(release: true, owner: leaseOwner);
                    contactRepository.SaveContact(newContact, options);
                }
                catch
                {
                    contactRepository.ReleaseContact(newContact.ContactId, leaseOwner);
                    throw;
                }
            }
        }
        catch
        {
            contactRepository.ReleaseContact(oldContact.ContactId, leaseOwner);
        }
    }
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.