Итеративная обработка данных
В Серверном API нет методов, реализующих пагинацию, как, например, в GET-запросах Table API. Без пагинации, при выборке большого количества записей из таблицы, мы можем столкнуться с ограничением на размер памяти ОЗУ, выделяемого на выполнение серверного скрипта.
Для решения данной проблемы можно разделить обработку данных на “порции” и создать скрипт по расписанию, который будет последовательно обрабатывать все записи в выборке. Для этого:
-
Создайте неактивный скрипт по расписанию с аналогичными настройками:
-
Укажите содержимое в скрипте:
(function executeScheduleScript() { let lastRecordId = '1'; const MESSAGE_PREFIX = 'Last record: '; const PROCESSING_LIMIT = 10; const TABLE_NAME = 'sys_translation'; const CONDITION = '(application_id=155931135900000002)'; const fiveMinutesAgo = new SimpleDateTime(); fiveMinutesAgo.addSeconds(-600); const logMessage = new SimpleRecord('sys_log'); logMessage.addQuery('level', 'debug'); logMessage.addQuery('message', 'STARTSWITH', MESSAGE_PREFIX); logMessage.addQuery('sys_created_at', '>', fiveMinutesAgo.getValue()); logMessage.orderByDesc('sys_created_at'); logMessage.selectAttributes('message'); logMessage.setLimit(1); logMessage.query(); if (logMessage.next() && logMessage.message.match(/\d+/g)) { lastRecordId = logMessage.message.match(/\d+/g)[0]; } function processRecords() { const record = new SimpleRecord(TABLE_NAME); record.addEncodedQuery(CONDITION); record.addQuery('sys_id', '>', lastRecordId); record.orderBy('sys_id'); record.setLimit(PROCESSING_LIMIT); record.query(); while (record.next()) { lastRecordId = record.sys_id; // ... processing actions } ss.debug(`${MESSAGE_PREFIX}${lastRecordId}`); } processRecords(); })(); -
Укажите нужные таблицу и условие выборки в переменные TABLE_NAME и CONDITION
-
Укажите скрипт, который будет обрабатывать записи вместо строки
// ... processing actions -
Активируйте скрипт по расписанию
-
Отслеживайте записи
sys_logс источником Debug /list/sys_log?condition=level=debug -
Скорректируйте значение PROCESSING_LIMIT в скрипте по расписанию, чтобы скрипт успевал обработать выборку до следующего запуска.
Постраничный доступ к данным
На основе представленной логики, можно организовать постраничную выборку данных, например, через функцию getNextPage, которая реализует одну итерацию пагинации (одну “страницу” данных) из таблицы, задаваемой через параметр tableName, начиная с записи после startRecordId. Начальное значение startRecordId должно быть равно максимально возможному sys_id, чтобы начать с новых записей, в противном случае на первой “странице” будут доступны наиболее старые записи.
function getNextPage(tableName, limit = 10, startRecordId = 'ffffffffffffffffffffffffffffffff') {
const record = new SimpleRecord(tableName);
record.addQuery('sys_id', '<', startRecordId);
record.orderByDesc('sys_id');
record.setLimit(limit);
record.selectAttributes(['sys_id', 'number']);
record.query();
const data = [];
let lastRecordId = startRecordId;
while (record.next()) {
lastRecordId = record.getValue('sys_id');
data.push(record.getAttributes());
}
return { data, lastRecordId }; // {"data":[],"lastRecordId":""}
}
Если нужно получить n-ую страницу, можно сделать обертку в цикл:
function getPage(tableName, pageNumber, pageSize = 10) {
let lastRecordId = 'ffffffffffffffffffffffffffffffff';
let currentPage = 1;
let page;
while (currentPage <= pageNumber) {
page = getNextPage(tableName, pageSize, lastRecordId);
lastRecordId = page.lastRecordId;
currentPage++;
}
return page; // {"data":[],"lastRecordId":""}
}
Важно заметить, что функция getPage неэффективна при больших значениях pageNumber, т.к. при попытке получить, например, 100-ю страницу, функция вызывает getNextPage 100 раз. Это не критично при малых объёмах, но сильно влияет на производительность при больших значениях pageNumber.
Table API
Проблему производительности можно решить воспользовавшись Table API и SimpleRestRequest. С помощью SimpleWebService sws.restRequestV1() создается объект SimpleRestRequest и далее с помощью его методов setRequestMethod, setRequestUrl и прочих, формируется запрос к Table API:
function getPage(tableName, pageNumber = 1, pageSize = 10) {
const request = sws.restRequestV1();
request.setRequestMethod('GET');
const instanceUri = ss.getProperty('simple.instance.uri');
request.setRequestUrl(`https://${instanceUri}/rest/v1/table/${tableName}`);
request.setQueryParameter('sysparm_fields', 'number,sys_id');
request.setRequestHeader('Authorization', `Bearer XXXXXXXX`);
request.setQueryParameter('sysparm_page', pageNumber.toString());
request.setQueryParameter('sysparm_limit', pageSize.toString());
const response = JSON.parse(request.execute().getBody());
return response; // {"status":"OK","data":[]}
}
Важно: Table API требует авторизации, поддерживаются Basic Auth и Bearer Token.
