Last active
October 31, 2022 06:23
-
-
Save jagmohansingh/78510b97f36c67f52fe5a8f8b16b98ec to your computer and use it in GitHub Desktop.
Server-side pagination using Apex on large datasets
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class LoadRecordsCtrl { | |
@RemoteAction | |
public static Map<String, Object> fetchRecords(String sObjectName, String lastKnownId, Integer pageSize, Integer direction) { | |
Map<String, Object> result = new Map<String, Object>(); | |
try { | |
SObject sObj = (SObject) Type.forName(sObjectName).newInstance(); | |
Integer totalRecords = SObjectQueryService.getTotalCount(sObjectName); | |
Integer totalPages = (Integer) Math.ceil((Decimal) totalRecords / pageSize); | |
Integer maxResults = pageSize; | |
if (String.isBlank(lastKnownId) && direction == -1) { | |
// when querying the last page, get the number of records on last page | |
maxResults = SObjectQueryService.getLastPageRecordCount(totalRecords, pageSize); | |
} | |
List<SObject> records = SObjectQueryService.queryRecords(sObjectName, lastKnownId, maxResults, direction); | |
// remove extra record and set next / previous state | |
result.putAll(SObjectQueryService.getPageState(records, lastKnownId, totalPages, pageSize, direction)); | |
// sort and set records | |
if (direction == -1) records.sort(); | |
result.put('totalRecords', totalRecords); | |
result.put('records', records); | |
result.put('status', true); | |
} catch (Exception ex) { | |
result.put('status', false); | |
result.put('error', ex.getMessage()); | |
} | |
return result; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public with sharing class SObjectQueryService { | |
public static Integer getTotalCount(String sObjectName) { | |
Integer totalCount = 0; | |
String query = 'select count(Id) totalCount from ' + sObjectName; | |
List<AggregateResult> res = (List<AggregateResult>) Database.query(query); | |
if (!res.isEmpty()) { | |
totalCount = (Integer) res.get(0).get('totalCount'); | |
} | |
return totalCount; | |
} | |
public static Integer getLastPageRecordCount(Integer totalRecords, Integer pageSize) { | |
Integer totalPages = (Integer) Math.ceil((Decimal) totalRecords / pageSize); | |
Integer recordCount = totalRecords - ((totalPages - 1) * pageSize); | |
return recordCount; | |
} | |
public static List<SObject> queryRecords(String sObjectName, String lastKnownId, Integer maxResults, Integer direction) { | |
List<String> fields = describeSObjectFields(sObjectName); | |
String query = 'select ' + String.join(fields, ','); | |
query += ' from ' + sObjectName; | |
if (String.isNotBlank(lastKnownId)) { | |
query += ' where Id ' + (direction == 1 ? '>' : '<') + ' \'' + lastKnownId + '\' '; | |
} | |
if (maxResults != null) { | |
// query one extra record | |
maxResults = maxResults + 1; | |
query += ' order by Id ' + (direction == 1 ? 'asc' : 'desc') + ' limit :maxResults'; | |
} | |
return Database.query(query); | |
} | |
public static Map<String, Object> getPageState(List<SObject> records, String lastKnownId, Integer totalPages, Integer pageSize, Integer direction) { | |
Map<String, Object> pageState = new Map<String, Object>(); | |
if (String.isBlank(lastKnownId)) { | |
if (records.size() > pageSize) { | |
records.remove(records.size() - 1); | |
} | |
if (direction == -1) { | |
// loading last page | |
pageState.put('hasPrevious', totalPages > 1); | |
pageState.put('hasNext', false); | |
} else { | |
// loading first page | |
pageState.put('hasPrevious', false); | |
pageState.put('hasNext', totalPages > 1); | |
} | |
} else if (records.size() > pageSize) { | |
records.remove(records.size() - 1); | |
pageState.put('hasPrevious', true); | |
pageState.put('hasNext', true); | |
} else { | |
if (direction == -1) { | |
// reached first page while navigating backwards | |
pageState.put('hasPrevious', false); | |
pageState.put('hasNext', true); | |
} else { | |
// reached last page while navigating forwards | |
pageState.put('hasPrevious', true); | |
pageState.put('hasNext', false); | |
} | |
} | |
if (!records.isEmpty()) { | |
// set first and last Id on the page | |
if (direction == -1) { | |
pageState.put('firstId', records.get(records.size() - 1).get('Id')); | |
pageState.put('lastId', records.get(0).get('Id')); | |
} else { | |
pageState.put('firstId', records.get(0).get('Id')); | |
pageState.put('lastId', records.get(records.size() - 1).get('Id')); | |
} | |
} | |
return pageState; | |
} | |
private static List<String> describeSObjectFields(String sObjectName) { | |
List<String> fields = new List<String>(); | |
SObject sObj = (SObject) Type.forName(sObjectName).newInstance(); | |
DescribeSObjectResult objResult = sObj.getSObjectType().getDescribe(); | |
Map<String, SObjectField> fieldsMap = objResult.fields.getMap(); | |
for (String field : fieldsMap.keySet()) { | |
DescribeFieldResult fieldResult = fieldsMap.get(field).getDescribe(); | |
if (fieldResult.isAccessible() && fieldResult.getType() != Schema.DisplayType.BASE64) { | |
fields.add(fieldResult.getName()); | |
if (fieldResult.getType() == Schema.DisplayType.REFERENCE) { | |
List<SObjectType> sObjectTypes = fieldResult.getReferenceTo(); | |
SObjectType referenceTo = sObjectTypes.get(0); | |
String relName = fieldResult.getRelationshipName(); | |
String nameField = getNameField(referenceTo); | |
fields.add(relName + '.' + nameField); | |
} | |
} | |
} | |
return fields; | |
} | |
private static String getNameField(SObjectType sObjType) { | |
String fieldName = null; | |
DescribeSObjectResult relObjResult = sObjType.getDescribe(); | |
Map<String, SObjectField> fieldsMap = relObjResult.fields.getMap(); | |
for (String field : fieldsMap.keySet()) { | |
DescribeFieldResult fieldResult = fieldsMap.get(field).getDescribe(); | |
if (fieldResult.isNameField()) { | |
fieldName = fieldResult.getName(); | |
break; | |
} | |
} | |
return fieldName; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment