@@ -20,7 +20,9 @@ import { DocumentKey } from '../model/document_key';
20
20
import {
21
21
FieldIndex ,
22
22
fieldIndexGetArraySegment ,
23
- fieldIndexGetDirectionalSegments
23
+ fieldIndexGetDirectionalSegments ,
24
+ IndexKind ,
25
+ IndexSegment
24
26
} from '../model/field_index' ;
25
27
import { FieldPath , ResourcePath } from '../model/path' ;
26
28
import {
@@ -304,70 +306,21 @@ export function targetGetLowerBound(
304
306
// For each segment, retrieve a lower bound if there is a suitable filter or
305
307
// startAt.
306
308
for ( const segment of fieldIndexGetDirectionalSegments ( fieldIndex ) ) {
307
- let segmentValue : ProtoValue | undefined = undefined ;
308
- let segmentInclusive = true ;
309
+ const segmentBound =
310
+ segment . kind === IndexKind . ASCENDING
311
+ ? targetGetAscendingBound ( target , segment , target . startAt )
312
+ : targetGetDescendingBound ( target , segment , target . startAt ) ;
309
313
310
- // Process all filters to find a value for the current field segment
311
- for ( const fieldFilter of targetGetFieldFiltersForPath (
312
- target ,
313
- segment . fieldPath
314
- ) ) {
315
- let filterValue : ProtoValue | undefined = undefined ;
316
- let filterInclusive = true ;
317
-
318
- switch ( fieldFilter . op ) {
319
- case Operator . LESS_THAN :
320
- case Operator . LESS_THAN_OR_EQUAL :
321
- filterValue = valuesGetLowerBound ( fieldFilter . value ) ;
322
- break ;
323
- case Operator . EQUAL :
324
- case Operator . IN :
325
- case Operator . GREATER_THAN_OR_EQUAL :
326
- filterValue = fieldFilter . value ;
327
- break ;
328
- case Operator . GREATER_THAN :
329
- filterValue = fieldFilter . value ;
330
- filterInclusive = false ;
331
- break ;
332
- case Operator . NOT_EQUAL :
333
- case Operator . NOT_IN :
334
- filterValue = MIN_VALUE ;
335
- break ;
336
- default :
337
- // Remaining filters cannot be used as lower bounds.
338
- }
339
-
340
- if ( valuesMax ( segmentValue , filterValue ) === filterValue ) {
341
- segmentValue = filterValue ;
342
- segmentInclusive = filterInclusive ;
343
- }
344
- }
345
-
346
- // If there is a startAt bound, compare the values against the existing
347
- // boundary to see if we can narrow the scope.
348
- if ( target . startAt !== null ) {
349
- for ( let i = 0 ; i < target . orderBy . length ; ++ i ) {
350
- const orderBy = target . orderBy [ i ] ;
351
- if ( orderBy . field . isEqual ( segment . fieldPath ) ) {
352
- const cursorValue = target . startAt . position [ i ] ;
353
- if ( valuesMax ( segmentValue , cursorValue ) === cursorValue ) {
354
- segmentValue = cursorValue ;
355
- segmentInclusive = target . startAt . inclusive ;
356
- }
357
- break ;
358
- }
359
- }
360
- }
361
-
362
- if ( segmentValue === undefined ) {
314
+ if ( ! segmentBound . value ) {
363
315
// No lower bound exists
364
316
return null ;
365
317
}
366
- values . push ( segmentValue ) ;
367
- inclusive &&= segmentInclusive ;
318
+ values . push ( segmentBound . value ) ;
319
+ inclusive &&= segmentBound . inclusive ;
368
320
}
369
321
return new Bound ( values , inclusive ) ;
370
322
}
323
+
371
324
/**
372
325
* Returns an upper bound of field values that can be used as an ending point
373
326
* when scanning the index defined by `fieldIndex`. Returns `null` if no
@@ -383,71 +336,147 @@ export function targetGetUpperBound(
383
336
// For each segment, retrieve an upper bound if there is a suitable filter or
384
337
// endAt.
385
338
for ( const segment of fieldIndexGetDirectionalSegments ( fieldIndex ) ) {
386
- let segmentValue : ProtoValue | undefined = undefined ;
387
- let segmentInclusive = true ;
339
+ const segmentBound =
340
+ segment . kind === IndexKind . ASCENDING
341
+ ? targetGetDescendingBound ( target , segment , target . endAt )
342
+ : targetGetAscendingBound ( target , segment , target . endAt ) ;
388
343
389
- // Process all filters to find a value for the current field segment
390
- for ( const fieldFilter of targetGetFieldFiltersForPath (
391
- target ,
392
- segment . fieldPath
393
- ) ) {
394
- let filterValue : ProtoValue | undefined = undefined ;
395
- let filterInclusive = true ;
344
+ if ( ! segmentBound . value ) {
345
+ // No upper bound exists
346
+ return null ;
347
+ }
348
+ values . push ( segmentBound . value ) ;
349
+ inclusive &&= segmentBound . inclusive ;
350
+ }
396
351
397
- switch ( fieldFilter . op ) {
398
- case Operator . GREATER_THAN_OR_EQUAL :
399
- case Operator . GREATER_THAN :
400
- filterValue = valuesGetUpperBound ( fieldFilter . value ) ;
401
- filterInclusive = false ;
402
- break ;
403
- case Operator . EQUAL :
404
- case Operator . IN :
405
- case Operator . LESS_THAN_OR_EQUAL :
406
- filterValue = fieldFilter . value ;
407
- break ;
408
- case Operator . LESS_THAN :
409
- filterValue = fieldFilter . value ;
410
- filterInclusive = false ;
411
- break ;
412
- case Operator . NOT_EQUAL :
413
- case Operator . NOT_IN :
414
- filterValue = MAX_VALUE ;
415
- break ;
416
- default :
417
- // Remaining filters cannot be used as upper bounds.
418
- }
352
+ return new Bound ( values , inclusive ) ;
353
+ }
419
354
420
- if ( valuesMin ( segmentValue , filterValue ) === filterValue ) {
421
- segmentValue = filterValue ;
422
- segmentInclusive = filterInclusive ;
423
- }
355
+ function targetGetAscendingBound (
356
+ target : Target ,
357
+ segment : IndexSegment ,
358
+ bound : Bound | null
359
+ ) : { value : ProtoValue | undefined ; inclusive : boolean } {
360
+ let value : ProtoValue | undefined = undefined ;
361
+ let inclusive = true ;
362
+
363
+ // Process all filters to find a value for the current field segment
364
+ for ( const fieldFilter of targetGetFieldFiltersForPath (
365
+ target ,
366
+ segment . fieldPath
367
+ ) ) {
368
+ let filterValue : ProtoValue | undefined = undefined ;
369
+ let filterInclusive = true ;
370
+
371
+ switch ( fieldFilter . op ) {
372
+ case Operator . LESS_THAN :
373
+ case Operator . LESS_THAN_OR_EQUAL :
374
+ filterValue = valuesGetLowerBound ( fieldFilter . value ) ;
375
+ break ;
376
+ case Operator . EQUAL :
377
+ case Operator . IN :
378
+ case Operator . GREATER_THAN_OR_EQUAL :
379
+ filterValue = fieldFilter . value ;
380
+ break ;
381
+ case Operator . GREATER_THAN :
382
+ filterValue = fieldFilter . value ;
383
+ filterInclusive = false ;
384
+ break ;
385
+ case Operator . NOT_EQUAL :
386
+ case Operator . NOT_IN :
387
+ filterValue = MIN_VALUE ;
388
+ break ;
389
+ default :
390
+ // Remaining filters cannot be used as lower bounds.
424
391
}
425
392
426
- // If there is a endAt bound, compare the values against the existing
427
- // boundary to see if we can narrow the scope.
428
- if ( target . endAt !== null ) {
429
- for ( let i = 0 ; i < target . orderBy . length ; ++ i ) {
430
- const orderBy = target . orderBy [ i ] ;
431
- if ( orderBy . field . isEqual ( segment . fieldPath ) ) {
432
- const cursorValue = target . endAt . position [ i ] ;
433
- if ( valuesMin ( segmentValue , cursorValue ) === cursorValue ) {
434
- segmentValue = cursorValue ;
435
- segmentInclusive = target . endAt . inclusive ;
436
- }
437
- break ;
393
+ if ( valuesMax ( value , filterValue ) === filterValue ) {
394
+ value = filterValue ;
395
+ inclusive = filterInclusive ;
396
+ }
397
+ }
398
+
399
+ // If there is an additional bound, compare the values against the existing
400
+ // range to see if we can narrow the scope.
401
+ if ( bound !== null ) {
402
+ for ( let i = 0 ; i < target . orderBy . length ; ++ i ) {
403
+ const orderBy = target . orderBy [ i ] ;
404
+ if ( orderBy . field . isEqual ( segment . fieldPath ) ) {
405
+ const cursorValue = bound . position [ i ] ;
406
+ if ( valuesMax ( value , cursorValue ) === cursorValue ) {
407
+ value = cursorValue ;
408
+ inclusive = bound . inclusive ;
438
409
}
410
+ break ;
439
411
}
440
412
}
413
+ }
441
414
442
- if ( segmentValue === undefined ) {
443
- // No upper bound exists
444
- return null ;
415
+ return { value, inclusive } ;
416
+ }
417
+
418
+ function targetGetDescendingBound (
419
+ target : Target ,
420
+ segment : IndexSegment ,
421
+ bound : Bound | null
422
+ ) : { value : ProtoValue | undefined ; inclusive : boolean } {
423
+ let value : ProtoValue | undefined = undefined ;
424
+ let inclusive = true ;
425
+
426
+ // Process all filters to find a value for the current field segment
427
+ for ( const fieldFilter of targetGetFieldFiltersForPath (
428
+ target ,
429
+ segment . fieldPath
430
+ ) ) {
431
+ let filterValue : ProtoValue | undefined = undefined ;
432
+ let filterInclusive = true ;
433
+
434
+ switch ( fieldFilter . op ) {
435
+ case Operator . GREATER_THAN_OR_EQUAL :
436
+ case Operator . GREATER_THAN :
437
+ filterValue = valuesGetUpperBound ( fieldFilter . value ) ;
438
+ filterInclusive = false ;
439
+ break ;
440
+ case Operator . EQUAL :
441
+ case Operator . IN :
442
+ case Operator . LESS_THAN_OR_EQUAL :
443
+ filterValue = fieldFilter . value ;
444
+ break ;
445
+ case Operator . LESS_THAN :
446
+ filterValue = fieldFilter . value ;
447
+ filterInclusive = false ;
448
+ break ;
449
+ case Operator . NOT_EQUAL :
450
+ case Operator . NOT_IN :
451
+ filterValue = MAX_VALUE ;
452
+ break ;
453
+ default :
454
+ // Remaining filters cannot be used as upper bounds.
455
+ }
456
+
457
+ if ( valuesMin ( value , filterValue ) === filterValue ) {
458
+ value = filterValue ;
459
+ inclusive = filterInclusive ;
445
460
}
446
- values . push ( segmentValue ) ;
447
- inclusive &&= segmentInclusive ;
448
461
}
449
462
450
- return new Bound ( values , inclusive ) ;
463
+ // If there is an additional bound, compare the values against the existing
464
+ // range to see if we can narrow the scope.
465
+ if ( bound !== null ) {
466
+ for ( let i = 0 ; i < target . orderBy . length ; ++ i ) {
467
+ const orderBy = target . orderBy [ i ] ;
468
+ if ( orderBy . field . isEqual ( segment . fieldPath ) ) {
469
+ const cursorValue = bound . position [ i ] ;
470
+ if ( valuesMin ( value , cursorValue ) === cursorValue ) {
471
+ value = cursorValue ;
472
+ inclusive = bound . inclusive ;
473
+ }
474
+ break ;
475
+ }
476
+ }
477
+ }
478
+
479
+ return { value, inclusive } ;
451
480
}
452
481
453
482
export abstract class Filter {
0 commit comments