我有一个 AppSheet 应用,它使用 Google Sheet 作为其表格的数据源,尽管 Google Sheet 的底层数据源是 Salesforce。Salesforce sObject 非常庞大(660 多列,数十万条记录),因此我无法使用 AppSheet/Salesforce 集成,因为当我只需要每个用户一小部分数据时,同步所有数据效率非常低。
我正在使用 Google Apps 脚本向 Salesforce 发送 GET 请求,该请求返回“Workers”的有效负载,通常少于一百行,每行 10 列。GET 请求选择应用程序登录用户的“地盘”中的 Workers。
Workers 表保存所有用户的草皮,因为在任何给定时间可能有多个用户登录,但应用程序仅在 UI 中呈现当前登录用户的草皮。
应用程序中没有可供用户修改工人记录的用户界面,只需要读取和选择工人记录以更新相关表(该表会写回 Salesforce)即可。因此,Salesforce 始终拥有工人的最新更新数据,我不需要与 Google 表格进行双向同步。
脚本可以运行,但速度非常慢。对于一组约 100 条记录(每条记录 10 列)的记录,整个 loadTurf 过程大约需要 30 秒,我正在尝试找出如何改进脚本以使其更快。我无法控制 Salesforce 身份验证/JWT 交换/GET 请求部分的速度,但该部分仅需 3-5 秒,其余部分是 API 有效负载返回后发生的处理:检查重复项、删除需要用新数据覆盖的行以及将新数据写入工作表。
我知道一定有更快的方法来做到这一点。检查重复项和删除部分似乎特别低效,每次用 迭代一行forEach
,但我无法找到批量处理删除的方法,因为草皮理论上可能处于非连续行中。有没有办法一次性删除整个过滤范围,而无需一次迭代一行?根据 Salesforce 有效负载检查每行是否有更新的列似乎比直接删除所有行并粘贴 Salesforce 返回的新行更慢。
无论如何,这是我的代码,最慢的部分似乎是检查重复和删除操作,对于一批 100 条记录,每个操作大约需要 10-11 秒。有人能建议一些改进方法吗?
const fieldsArray = [ ... list of fields ... ];
const ss = SpreadsheetApp.openByUrl( ... url ...);
const workers = ss.getSheetByName( ... sheetName ...);
const contactIds = workers.getRange("A2:A").getValues().flat().filter(Boolean);
async function loadUserTurf(employer) {
let records;
if (employer) {
try {
const qp = new QueryParameters();
qp.setSelect(fieldsArray.toString());
qp.setFrom("Contact");
qp.setWhere(`Employer_Name_Text__c = \'${employer}\' AND Active_Worker__c = TRUE`);
records = await get(qp);
setUserTurf(employer, records);
} catch (err) {
logErrorFunctions('loadUserTurf', employer, records, err);
}
} else {
console.log(`loadUserTurf > 24: no employer provided`);
}
}
const confirmUniqueContactId = (id) => !contactIds.includes(id);
function appendNewRows(data, sheet) { // data = array of objects
try {
data.forEach(obj => {
if (confirmUniqueContactId(obj.Id)) {
// flatten object to array
const row = Object.values(obj).slice(1);
sheet.appendRow(row);
}
})
} catch (err) {
logErrorFunctions('appendNewRows', [data, sheet], '', err);
}
}
function setUserTurf(employerName, payload) {
// Check for matching rows -- has this turf been pulled before?
// create an array of all matching row indices so we can delete them
// and replace them with fresh data from Salesforce
const allData = workers.getDataRange().getValues();
const turfIndices = allData.map((row, index) => {
if (row[3] === employerName) {
return index + 1;
}
}).filter(n => n); // remove null values
// If no matching rows found (user's first login)
// create new rows to append the payload from Salesforce
if (!turfIndices.length) {
// append new rows with data from payload from loadTurf function
try {
appendNewRows(payload, workers);
} catch (err) {
console.log(err);
logErrorFunctions('setUserTurf', turfIndices, '', err);
}
} else {
// otherwise, delete all existing rows in that turf and replace them with fresh data from Salesforce
// because checking for differences at the individual cell level seems even more inefficient?
try {
turfIndices.forEach(index => workers.deleteRow(index));
// append new rows with data from payload from loadTurf function
appendNewRows(payload, workers);
} catch (err) {
logErrorFunctions('setUserTurf', turfIndices, '', err);
}
}
}