@@ -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
0 commit comments