-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathMergeContactsByName.cls
127 lines (101 loc) · 4.59 KB
/
MergeContactsByName.cls
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
global class MergeContactsByName implements Database.Batchable<String>, Database.Stateful {
List<String> fieldsToMerge = null;
global Integer recordsProcessed = 0;
global Iterable<String> start (Database.BatchableContext BC){
//we have only several thouthands of records. so it is ok
//for millions of records use AggregateResultIterable
//https://www.xgeek.net/salesforce/using-aggregate-soql-queries-results-in-batch-apex/
List<String> result = new List<String>();
for(AggregateResult row: [SELECT Name FROM Contact GROUP BY Name HAVING COUNT(Id) > 1]){
result.add((String)row.get('Name'));
}
return result;
}
global void execute(Database.BatchableContext bc, List<String> names){
List<Contact> forDelete = new List<Contact>();
List<Contact> forUpdate = new List<Contact>();
//construct query
List<String> fieldsForMerge = getFieldsForMerge();
String query = 'SELECT Id, Name, ' + join(fieldsForMerge, ',') + ' FROM CONTACT WHERE Name IN :names ORDER BY CreatedDate DESC';
List<Contact> rawContactsList = Database.query(query);
//group contacts by name
Map<String, List<Contact>> contactsGrouped = groupContactsByName(rawContactsList);
//for each group
for(String name: contactsGrouped.keySet()){
List<Contact> contactsToMerge = contactsGrouped.get(name);
//the lastest one should stay, other ones should be deleted
Contact newestContact = contactsToMerge.get(0);
contactsToMerge.remove(0);
//merge information from rest contacts
for(Contact contact: contactsToMerge){
newestContact = mergeContacts(contact, newestContact);
forDelete.add(contact);
}
forUpdate.add(newestContact);
}
delete forDelete;
updateDML(forUpdate);
}
//update with ignore duplicates option
void updateDML(List<sObject> items){
Database.DMLOptions dmlOptions = new Database.DMLOptions();
dmlOptions.DuplicateRuleHeader.AllowSave = true;
List<Database.SaveResult> results = Database.update(items, dmlOptions);
for(Database.SaveResult result: results){
if (!result.isSuccess()) {
System.debug('Error on updating items');
System.assert(false);
}
}
}
String join(List<String> values, String delimeter) {
List<String> valueCopy = new List<String>(values);
if(valueCopy.isEmpty()){
return null;
}
String result = valueCopy[0];
valueCopy.remove(0);
while(!valueCopy.isEmpty()) {
result += delimeter + valueCopy[0];
valueCopy.remove(0);
}
return result;
}
Contact mergeContacts(Contact source, Contact destination){
List<String> fieldsForMerge = getFieldsForMerge();
for(String fieldName: fieldsForMerge){
Object oldValue = destination.get(fieldName);
if(oldValue == null || oldValue == ''){
destination.put(fieldName, source.get(fieldName));
}
}
return destination;
}
List<String> getFieldsForMerge(){
//describe calls are heavy, lets cache them
if(fieldsToMerge == null){
fieldsToMerge = new List<String>();
for(SObjectField field: Contact.getSObjectType().getDescribe().fields.getMap().values()){
Schema.DescribeFieldResult fieldDescribed = field.getDescribe();
//every field that could be changed
if(fieldDescribed.isUpdateable()){
fieldsToMerge.add(fieldDescribed.getName());
}
}
}
return fieldsToMerge;
}
Map<String, List<Contact>> groupContactsByName(List<Contact> rawContactsList){
Map<String, List<Contact>> contactsGrouped = new Map<String, List<Contact>>();
for(Contact contact: rawContactsList){
if(!contactsGrouped.containsKey(contact.Name)){
contactsGrouped.put(contact.Name, new List<Contact>());
}
contactsGrouped.get(contact.Name).add(contact);
}
return contactsGrouped;
}
global void finish(Database.BatchableContext bc){
System.debug(recordsProcessed + ' records processed. Shazam!');
}
}