14

I'm hoping to leverage SF's existing fuzzy matching capability specifically with regards to addresses during a SOQL query or some other lookup mechanism. Before I just implement their solution for myself I'm hoping the functionality is exposed somewhere.

See: Matching Methods Used with Matching Rules

Specifically their fuzzy street matching algorithm (weighting number, street address, and suffix differently).

A lot of my search hits for fuzzy matching or searching have resulted in the effort to 'dedupe' data and none seem to have mentioned SF's built in matching capabilities so I'm pretty pessimistic.

To clarify, this is not in order to prevent duplicates from being inserted. Instead, this is in order to present multiple matches to a consuming service.

5
  • did you create the matching and duplicate rules with criteria specified and activated it? 'coz i used it earlier and seemed to be working. I even implemented it on vf page with the apex as well. It was working fine. Not sure if it changed now. Can you post your rules that you created if anything is missing?
    – Anurag
    Commented Jun 15, 2016 at 19:05
  • @AnuragA Edited my question for clarity. To be clear I want to be able to look up records. Whether it be via SOQL or some other mechanism, I want to look up records doing so in the same manner that the duplicate matching might verify duplicates. This is in order to present multiple options to the user of a web service, not in order to prevent duplicates from being inserted. Commented Jun 15, 2016 at 19:09
  • Any luck with this, @ninwa? I've had the same question for quite some time. Commented Apr 17, 2017 at 17:54
  • @BrentBow - Unfortunately not. However, Apex does offer built-in edit distance calculation for strings. I ended up rolling my own solution similar to SFDC. First I query with something along the lines of 'like %street_name%'. I then weigh different components of the returned addresses (street number, street name, and suffix) as a component of their edit distance compared to the original search. I calculate a confidence based on this and return the list in order of descending confidence. Good luck! Commented Apr 19, 2017 at 17:19
  • Ya, I saw the String instance method getLevenshteinDistance() that could be used for this, but boy that's a lot to custom roll, given that SF has the logic if it were only exposed via the API :/ Thanks for responding Commented Apr 19, 2017 at 17:33

3 Answers 3

9

Winter '18 promises to bring the findDuplicates() method to Apex -- this has been available to the SOAP API for some time but now you can access the list of duplicate candidates in Apex.

https://developer.salesforce.com/docs/atlas.en-us.210.0.apexcode.meta/apexcode/apex_class_Datacloud_FindDuplicates.htm#apex_class_Datacloud_FindDuplicates

There were some previous 'workarounds' that involved trying to INSERT a record and inspect the Exception class to see if it was rejected due to a Duplicate Rule being violated. The findDuplicates() method is far cleaner

3

Yes you can. We are doing it right now and it works like a charm.

If you do Database.insert, you can capture duplicate rule exceptions in your APEX code and do something when that happens.

Moreover, if you use a custom object as a 'test object', you create the matching rule using this object against some other object (like account if you want to check for duplicate accounts).

In your apex code, you try to insert a record into the test object and if that fails due to the duplicate rule, you capture this exception etc.

List<TestObject__c> testObjects ....

saveResults = Database.insert(testObjects, false);

for (Database.Error error : saveResult.getErrors())
  if (error instanceof Database.DuplicateError){ ... }

We even have the opposite where the triggering of a duplicate rule is a 'positive' event.

1

You can use apex to show the duplicate records if any exists, that match your criteria, I got this working with the following code:

VF Page:

        <apex:pageBlock title="LeadDetail">

         <apex:pageBlockButtons location="top">
        <apex:commandButton value="Search" action="{!Search}" />
        <apex:commandButton value="Create" action="{!save}"/>
        </apex:pageBlockButtons>         

        <apex:pageBlockSection >
            <apex:pageBlockSectionItem >
            <apex:outputlabel value="Last Name" for="rep"/>
            <apex:inputField id="rep" value="{!newlead.lastname}" />
        </apex:pageBlockSectionItem>  
            <apex:inputField value="{!lead.company}" rendered="{!showmsg }"/>
            <apex:inputField value="{!lead.status}" rendered="{!showmsg }"/> 

      </apex:pageBlockSection>


     <apex:pageBlock title="Duplicate Records" rendered="{!hasDuplicateResult}">
        <apex:pageMessages />
        <apex:outputPanel layout="block" style="overflow:auto;width:750px;height:250px" >

        <apex:pageblocksection >

        <apex:pageblockTable value="{!test}" var="ac" columns="2" rendered="{!showmsg}">

<apex:column >
                <apex:facet name="header">Name</apex:facet>
               <apex:outputText value="{!ac.name}"></apex:outputText>
            </apex:column> 

           <apex:column >
           <apex:facet name="header">Status                         
                </apex:facet>
                <apex:outputText value="{!ac.status}"></apex:outputText>
           </apex:column>


</apex:pageblockTable>       
    </apex:pageblocksection>
    </apex:pageBlock>



        <apex:pageBlockTable value="{!duplicateRecords}" var="item" >
            <apex:column >
                <apex:facet name="header">Name</apex:facet>
                <apex:outputLink value="/{!item['Id']}">{!item['Name']}</apex:outputLink>
            </apex:column>
            <apex:column >
                <apex:facet name="header">Owner</apex:facet>
                <apex:outputField value="{!item['company']}"/>
            </apex:column>
               <apex:column >
             <apex:facet name="header">Last Modified Date</apex:facet>
                <apex:outputField value="{!item['LastModifiedDate']}"/>
            </apex:column>
      </apex:pageBlockTable> 


       </apex:outputPanel>
    </apex:pageBlock>


</apex:form> 

Apex class:

public class accountDedupeController {


 Private final Lead led;
 public lead newlead{get;set;}
public accountDedupeController(ApexPages.StandardController stdController) {
    this.led = (Lead)stdController.getRecord();
    showmsg = false;
    system.debug('3333333333333'+led.Lastname);
}
Public boolean showmsg{get;set;}    

public list<lead> test{get;set;}

public pagereference search(){
    system.debug('3333333333333'+led.Lastname);
    showmsg = true;
    string query;
    query = 'select id, name, firstname,lastname, status from lead where lastname like'+ '\'%'+ led.Lastname +'%\'';

    test= database.query(query);
    system.debug('22222222222'+query);
    system.debug('111111'+test+test.size());

    return null;
}

// Initialize a variable to hold the account record you're processing
private final lead lead;

// Initialize a list to hold any duplicate records
private List<sObject> duplicateRecords;

// Define variable that’s true if there are duplicate records
public boolean hasDuplicateResult{get;set;}

// Define the constructor
public accountDedupeController() {

    // Define the values for the account you’re processing based on its ID
    Id id = ApexPages.currentPage().getParameters().get('id');
    this.lead = (id == null) ? new lead() : 
        [SELECT Id, Name
         FROM Lead WHERE Id = :id];

    // Initialize empty list of potential duplicate records
    this.duplicateRecords = new List<sObject>();
    this.hasDuplicateResult = false;
}



 // Return account and its values to the Visualforce page for display
   public lead getlead() {
        return this.lead;
    }

    // Return duplicate records to the Visualforce page for display
    public List<sObject> getDuplicateRecords() {
        return this.duplicateRecords;
    }

// Process the saved record and handle any duplicates
public PageReference save() {

    // Optionally, set DML options here, use “DML” instead of “false” 
    //   in the insert()
     Database.DMLOptions dml = new Database.DMLOptions(); 
     dml.DuplicateRuleHeader.allowSave = true;
     dml.DuplicateRuleHeader.includeRecordDetails = true;
     dml.DuplicateRuleHeader.runsAsCurrentUser = true;
    Database.SaveResult saveResult = Database.insert(lead, false);

    if (!saveResult.isSuccess()) {
        for (Database.Error error : saveResult.getErrors()) {
            // If there are duplicates, an error occurs
            // Process only duplicates and not other errors 
            //   (e.g., validation errors)
            if (error instanceof Database.DuplicateError) {
                // Handle the duplicate error by first casting it as a 
                //   DuplicateError class
                // This lets you use methods of that class 
                //  (e.g., getDuplicateResult())
                Database.DuplicateError duplicateError = 
                        (Database.DuplicateError)error;
                Datacloud.DuplicateResult duplicateResult = 
                        duplicateError.getDuplicateResult();

                // Display duplicate error message as defined in the duplicate rule
                ApexPages.Message errorMessage = new ApexPages.Message(
                        ApexPages.Severity.ERROR, 'Duplicate Error: ' + 
                        duplicateResult.getErrorMessage());
                ApexPages.addMessage(errorMessage);

                // Get duplicate records
                this.duplicateRecords = new List<sObject>();

                // Return only match results of matching rules that 
                //  find duplicate records
                Datacloud.MatchResult[] matchResults = 
                        duplicateResult.getMatchResults();

                // Just grab first match result (which contains the 
                //   duplicate record found and other match info)
                Datacloud.MatchResult matchResult = matchResults[0];

                Datacloud.MatchRecord[] matchRecords = matchResult.getMatchRecords();

                // Add matched record to the duplicate records variable
                for (Datacloud.MatchRecord matchRecord : matchRecords) {
                    System.debug('MatchRecord: ' + matchRecord.getRecord());
                    this.duplicateRecords.add(matchRecord.getRecord());
                }
                this.hasDuplicateResult = !this.duplicateRecords.isEmpty();
            }
        }

        //If there’s a duplicate record, stay on the page
        return null;
    }

    //  After save, navigate to the view page:
    return (new ApexPages.StandardController(lead)).view();
    }

}

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .