@@ -159,6 +159,10 @@ export class ScanOperator implements Operator {
159159 private prefetchFragIdx = - 1 ;
160160 private prefetchSkipped = 0 ;
161161 private _filterColNames : Set < string > | null = null ;
162+ /** Cached per-fragment column splits (avoids .filter() per page on hot path). */
163+ private _cachedFragIdx = - 1 ;
164+ private _cachedFilterCols : ColumnMeta [ ] = [ ] ;
165+ private _cachedProjCols : ColumnMeta [ ] = [ ] ;
162166
163167 constructor ( fragments : FragmentSource [ ] , query : QueryDescriptor , wasm : WasmEngine , applyFilters = false ) {
164168 this . fragments = fragments ;
@@ -286,8 +290,18 @@ export class ScanOperator implements Operator {
286290 const hasFilters = allFilters . length > 0 || ( allFilterGroups && allFilterGroups . length > 0 ) ;
287291 if ( this . filtersApplied && hasFilters ) {
288292 const fcn = this . _filterColNames ! ;
289- const filterCols = frag . columns . filter ( c => fcn . has ( c . name ) ) ;
290- const projCols = frag . columns . filter ( c => ! fcn . has ( c . name ) ) ;
293+ // Cache column splits per fragment (avoids repeated .filter() per page)
294+ if ( this . _cachedFragIdx !== this . fragIdx ) {
295+ this . _cachedFragIdx = this . fragIdx ;
296+ this . _cachedFilterCols = [ ] ;
297+ this . _cachedProjCols = [ ] ;
298+ for ( const c of frag . columns ) {
299+ if ( fcn . has ( c . name ) ) this . _cachedFilterCols . push ( c ) ;
300+ else this . _cachedProjCols . push ( c ) ;
301+ }
302+ }
303+ const filterCols = this . _cachedFilterCols ;
304+ const projCols = this . _cachedProjCols ;
291305
292306 // Phase 1: Fetch + decode only filter columns
293307 let filterPageMap : Map < string , { buf : ArrayBuffer ; pageInfo : PageInfo } > ;
@@ -329,11 +343,12 @@ export class ScanOperator implements Operator {
329343 projDecoded = new Map < string , DecodedValue [ ] > ( ) ;
330344 }
331345
332- const allDecoded = new Map ( [ ...filterDecoded , ...projDecoded ] ) ;
346+ // Merge proj columns into filter map (avoids Map spread allocation)
347+ for ( const [ k , v ] of projDecoded ) filterDecoded . set ( k , v ) ;
333348 this . scanMs += Date . now ( ) - scanStart ;
334349
335350 if ( matchingIndices . length > 0 ) {
336- return { columns : allDecoded , rowCount, selection : matchingIndices } ;
351+ return { columns : filterDecoded , rowCount, selection : matchingIndices } ;
337352 }
338353 continue ;
339354 }
0 commit comments