Skip to content

Commit bf8de0a

Browse files
authored
Manage zendesk search with more than 1000 results (#77)
Signed-off-by: Denis Jannot <denis.jannot@solo.io>
1 parent d4bbfab commit bf8de0a

3 files changed

Lines changed: 42 additions & 15 deletions

File tree

doc2vec.ts

Lines changed: 39 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1476,19 +1476,15 @@ export class Doc2Vec {
14761476

14771477
// Build query parameters — use the status filter for the search query
14781478
const statusList = Array.from(statusFilter);
1479-
const query = `updated>${lastRunDate.split('T')[0]} status:${statusList.join(',status:')}`;
1479+
const statusClause = `status:${statusList.join(',status:')}`;
14801480

1481-
let nextPage: string | null = `${baseUrl}/search.json?query=${encodeURIComponent(query)}&sort_by=updated_at&sort_order=asc`;
1481+
// Zendesk /search.json caps results at 1000 (10 pages * 100). Bisect the date range
1482+
// into smaller windows whenever a window would exceed that cap, so we never hit page 11.
14821483
let totalTickets = 0;
14831484
let failedTickets = 0;
14841485

1485-
while (nextPage) {
1486-
const data = await fetchWithRetry(nextPage);
1487-
const tickets = data.results || [];
1488-
1489-
logger.info(`Processing batch of ${tickets.length} tickets`);
1490-
1491-
for (const ticket of tickets) {
1486+
const processResults = async (results: any[]) => {
1487+
for (const ticket of results) {
14921488
try {
14931489
await processTicket(ticket);
14941490
totalTickets++;
@@ -1497,13 +1493,44 @@ export class Doc2Vec {
14971493
logger.error(`Failed to process ticket #${ticket.id}, will retry next run: ${error.message}`);
14981494
}
14991495
}
1496+
};
15001497

1501-
nextPage = data.next_page || null;
1498+
// Work queue of [start, end) date windows (ISO strings). Seed with [lastRunDate, syncStartDate).
1499+
const windows: Array<[string, string]> = [[lastRunDate, syncStartDate]];
1500+
const MAX_PER_WINDOW = 1000;
15021501

1503-
if (nextPage) {
1502+
while (windows.length > 0) {
1503+
const [wStart, wEnd] = windows.shift()!;
1504+
const q = `updated>${wStart} updated<${wEnd} ${statusClause}`;
1505+
const firstUrl = `${baseUrl}/search.json?query=${encodeURIComponent(q)}&sort_by=updated_at&sort_order=asc`;
1506+
1507+
logger.debug(`Searching window ${wStart} .. ${wEnd}`);
1508+
const firstData = await fetchWithRetry(firstUrl);
1509+
const count = firstData?.count ?? 0;
1510+
1511+
if (count > MAX_PER_WINDOW) {
1512+
const startMs = new Date(wStart).getTime();
1513+
const endMs = new Date(wEnd).getTime();
1514+
if (endMs - startMs <= 1000) {
1515+
logger.warn(`Window ${wStart} .. ${wEnd} has ${count} tickets but is already ≤1s wide — processing first 1000 only, some may be missed`);
1516+
} else {
1517+
const midIso = new Date(startMs + Math.floor((endMs - startMs) / 2)).toISOString();
1518+
logger.debug(`Window has ${count} tickets (>${MAX_PER_WINDOW}) — bisecting at ${midIso}`);
1519+
windows.unshift([wStart, midIso], [midIso, wEnd]);
1520+
continue;
1521+
}
1522+
}
1523+
1524+
logger.info(`Processing window ${wStart}..${wEnd}: ${count} tickets`);
1525+
await processResults(firstData.results || []);
1526+
1527+
let nextPage: string | null = firstData.next_page || null;
1528+
while (nextPage) {
15041529
logger.debug(`Fetching next page: ${nextPage}`);
1505-
// Rate limiting: wait between requests
15061530
await new Promise(res => setTimeout(res, 1000));
1531+
const data = await fetchWithRetry(nextPage);
1532+
await processResults(data.results || []);
1533+
nextPage = data.next_page || null;
15071534
}
15081535
}
15091536

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "doc2vec",
3-
"version": "2.10.0",
3+
"version": "2.10.1",
44
"type": "commonjs",
55
"description": "",
66
"main": "dist/doc2vec.js",

0 commit comments

Comments
 (0)