· 2 years ago · Sep 22, 2023, 01:55 PM
1/********************************Sample Data***************************/
2
3//raw data
4[
5 {
6 FirstName: 'ABC Contact',
7 .......,
8 Account: {
9 Name: 'XYZ Account',
10 .......
11 },
12 Cases: {
13 records: [
14 { CaseNumber: 'case101', Status: 'Open' },
15 { CaseNumber: 'case102', Status: 'Closed' }
16 ......
17 ]
18 },
19 },
20 ...
21]
22
23
24//output/desired data
25[
26 { FirstName: 'ABC Contact', 'Account.Name': 'XYZ Account', 'Cases.CaseNumber': 'case101', 'Cases.Status': 'Open', .... },
27 { FirstName: 'ABC Contact', 'Account.Name': 'XYZ Account', 'Cases.CaseNumber': 'case102', 'Cases.Status': 'Closed', ..... },
28 ...
29]
30
31
32/********************************Main Function***************************/
33
34//an example query. normally this query is generated dynamically through the OpenAI and returned to APEX.
35String query = 'select firstname, lastname, account.name, account.billingcity, (SELECT caseNumber, status FROM Cases) from contact limit 1000';
36
37//the returned query is executed dynamically.
38List<SObject> data = Database.query(query);
39
40//then it is flattened to manage relationships data.
41data = LazyAdminSearchLog.dataFlattener(data);
42
43//here, the data is passed to LWC for display...
44
45
46
47
48
49
50/********************************Recurrance Methods to Flatten the data in single list of objects***************************/
51
52 /**
53 * @description - method to flatten the data from child-to-parent and parent-to-child relationships.
54 * @param data - the data to flattern
55 * @return - List<Object> - the list of data as object, similar to what was given is returrned but flattened.
56 */
57 public static List<Object> dataFlattener(List<Object> data) {
58 List<Map<String, Object>> flattenedData = new List<Map<String, Object>>();
59 if(data == null) return flattenedData;
60
61 try {
62
63 // Convert data to JSON so it can be converted into maps.
64 String jsonData = JSON.serialize(data);
65 Object obj = JSON.deserializeUntyped(jsonData);
66 List<Map<String, Object>> jsonMaps = new List<Map<String, Object>>();
67
68 //make a map from deserialized json object.
69 for (Object instance : (List<Object>)obj) {
70 jsonMaps.add((Map<String, Object>)instance);
71 }
72
73 //now, flatten each record's data. as each record can have parent-to-child and vice versa relationships.
74 for (Map<String, Object> jsonMap : jsonMaps) {
75 flattenedData.addAll(recursiveFlattener(jsonMap));
76 }
77
78 } catch (Exception ex) {
79 ErrorHandler.throwCatchedErrors(ex.getStackTraceString(), ex.getMessage(), +ex.getCause());
80 }
81 return flattenedData;
82 }//dataflattener ends
83
84 /**
85 * @description - recursive method to iterate over flattening data.
86 * @param input - the key-value pair to iterate against.
87 * @return - List<Map<String, Object>> - list of key-value pairs of the records data, appended into the result.
88 */
89 public static List<Map<String, Object>> recursiveFlattener(Map<String, Object> input) {
90 List<Map<String, Object>> results = new List<Map<String, Object>>();
91 Map<String, Object> parentFields = new Map<String, Object>();
92
93 try {
94 //pass the input object to recursive method for the first time.
95 //the main object doesn't have a prefix.
96 //data is returned in parentFields
97 flattenHelper(input, '', parentFields);
98
99 //if it is a parent-to-child relationship, then it contains a unique field "records".
100 if(parentFields.containsKey('records')) {
101 //for p-to-c relationship, we have to append each child record to parent's record.
102 for(Object childObj : (List<Object>)parentFields.get('records')) {
103
104 Map<String, Object> childFields = (Map<String, Object>) childObj;
105 Map<String, Object> combined = parentFields.clone();
106
107 //get the object name to use as the prefix - <prefix>.<field>. i.e. Case.Id
108 Id recordId = (Id)childFields.get('Id'); // Example ID
109 Schema.SObjectType sObjectType = recordId.getSObjectType();
110 String childObjectName = sObjectType.getDescribe().getName();
111
112 //create a new map with appended prefix with the field names (keys)
113 Map<String, Object> childRelationFields = new Map<String, Object>();
114
115 for( String key: childFields.keySet()) {
116 if(key.length() > 2 && key.endsWith('Id')) continue;
117 childRelationFields.put(childObjectName+'.'+key.replace('records.', ''), childFields.get(key));
118 }
119
120 //time to combine child records with parent records
121 combined.putAll(childRelationFields);
122 combined.remove('records');
123 //now we have to add the combined data into the output result list.
124 results.add(combined);
125 }
126 } else {
127 //otherwise, simply add the data into the results. considering no relationship exists.
128 results.add(parentFields);
129 }
130
131 } catch (Exception ex) {
132 ErrorHandler.throwCatchedErrors(ex.getStackTraceString(), ex.getMessage(), +ex.getCause());
133 }
134 return results;
135 }
136
137 /**
138 * @description - recursive helper method. Which actually performs the operation to flatten the data.
139 * @param current - the key-value pair to iterate against.
140 * @param prefix - prefix of the parent object
141 * @param result - the iteration result
142 */
143 public static void flattenHelper(Map<String, Object> current, String prefix, Map<String, Object> result) {
144
145 //if current record is empty
146 if(current == null) return;
147 try {
148 //iterate for each record to determine whether it is a normal record, a parent-to-child record or vice versa.
149 for (String key : current.keySet()) {
150 //we need to filter/skip some values for record. unnecessary fields actually.
151 if (key.toLowerCase() == 'attributes' || key.toLowerCase() == 'done' || key.toLowerCase() == 'totalsize' ) continue;
152
153 //get the value for key / field api name.
154 Object value = current.get(key);
155
156 //checking if it is a parent-to-child relationship, than we need to recur/redo everything for each child record.
157 if (key.toLowerCase().contains('records')) {
158 //add the result from recurrence. i.e. restart the process. serialize, map, flatten and filter children records.
159 result.put(key, dataFlattener((List<Object>)value));
160 //checkinf if it is a child-to-parent relationship, then we flatten data by adding it into main map.
161 //through recurrence it is!
162 } else if (value instanceof Map<String, Object>) {
163 flattenHelper((Map<String, Object>) value, prefix + key + '.', result);
164 } else {
165 //if it is a normal record, which becomes condition at the last for each recurrence.
166 //then simply add it into the result list. this flattens the data.
167 result.put(prefix + key, value);
168 }
169 }
170 } catch (Exception ex) {
171 ErrorHandler.throwCatchedErrors(ex.getStackTraceString(), ex.getMessage(), +ex.getCause());
172 }
173 }