@@ -29,15 +29,14 @@ import {
29
29
queryEqual ,
30
30
SetOptions
31
31
} from '../../../lite/src/api/reference' ;
32
- import {
33
- changesFromSnapshot ,
34
- SnapshotMetadata
35
- } from '../../../src/api/database' ;
32
+ import { SnapshotMetadata } from '../../../src/api/database' ;
36
33
import { Code , FirestoreError } from '../../../src/util/error' ;
37
- import { ViewSnapshot } from '../../../src/core/view_snapshot' ;
34
+ import { ChangeType , ViewSnapshot } from '../../../src/core/view_snapshot' ;
38
35
import { FieldPath } from '../../../lite/src/api/field_path' ;
39
36
import { SnapshotListenOptions } from './reference' ;
40
37
import { UntypedFirestoreDataConverter } from '../../../src/api/user_data_reader' ;
38
+ import { debugAssert , fail } from '../../../src/util/assert' ;
39
+ import { newQueryComparator } from '../../../src/core/query' ;
41
40
42
41
/**
43
42
* Converter used by `withConverter()` to transform user objects of type `T`
@@ -403,17 +402,108 @@ export class QuerySnapshot<T = DocumentData> {
403
402
! this . _cachedChanges ||
404
403
this . _cachedChangesIncludeMetadataChanges !== includeMetadataChanges
405
404
) {
406
- this . _cachedChanges = changesFromSnapshot < QueryDocumentSnapshot < T > > (
405
+ this . _cachedChanges = this . changesFromSnapshot (
407
406
this . _snapshot ,
408
- includeMetadataChanges ,
409
- this . _convertToDocumentSnapshot . bind ( this )
407
+ includeMetadataChanges
410
408
) ;
411
409
this . _cachedChangesIncludeMetadataChanges = includeMetadataChanges ;
412
410
}
413
411
414
412
return this . _cachedChanges ;
415
413
}
416
414
415
+ /**
416
+ * Calculates the array of DocumentChanges for a given ViewSnapshot.
417
+ *
418
+ * Exported for testing.
419
+ *
420
+ * @param snapshot The ViewSnapshot that represents the expected state.
421
+ * @param includeMetadataChanges Whether to include metadata changes.
422
+ * @param converter A factory function that returns a QueryDocumentSnapshot.
423
+ * @return An object that matches the DocumentChange API.
424
+ */
425
+ changesFromSnapshot (
426
+ snapshot : ViewSnapshot ,
427
+ includeMetadataChanges : boolean
428
+ ) : Array < DocumentChange < T > > {
429
+ if ( snapshot . oldDocs . isEmpty ( ) ) {
430
+ // Special case the first snapshot because index calculation is easy and
431
+ // fast
432
+ let lastDoc : Document ;
433
+ let index = 0 ;
434
+ return snapshot . docChanges . map ( change => {
435
+ const doc = this . _convertToDocumentSnapshot (
436
+ change . doc ,
437
+ snapshot . fromCache ,
438
+ snapshot . mutatedKeys . has ( change . doc . key )
439
+ ) ;
440
+ debugAssert (
441
+ change . type === ChangeType . Added ,
442
+ 'Invalid event type for first snapshot'
443
+ ) ;
444
+ debugAssert (
445
+ ! lastDoc ||
446
+ newQueryComparator ( snapshot . query ) ( lastDoc , change . doc ) < 0 ,
447
+ 'Got added events in wrong order'
448
+ ) ;
449
+ lastDoc = change . doc ;
450
+ return {
451
+ type : 'added' as DocumentChangeType ,
452
+ doc,
453
+ oldIndex : - 1 ,
454
+ newIndex : index ++
455
+ } ;
456
+ } ) ;
457
+ } else {
458
+ // A DocumentSet that is updated incrementally as changes are applied to use
459
+ // to lookup the index of a document.
460
+ let indexTracker = snapshot . oldDocs ;
461
+ return snapshot . docChanges
462
+ . filter (
463
+ change =>
464
+ includeMetadataChanges || change . type !== ChangeType . Metadata
465
+ )
466
+ . map ( change => {
467
+ const doc = this . _convertToDocumentSnapshot (
468
+ change . doc ,
469
+ snapshot . fromCache ,
470
+ snapshot . mutatedKeys . has ( change . doc . key )
471
+ ) ;
472
+ let oldIndex = - 1 ;
473
+ let newIndex = - 1 ;
474
+ if ( change . type !== ChangeType . Added ) {
475
+ oldIndex = indexTracker . indexOf ( change . doc . key ) ;
476
+ debugAssert ( oldIndex >= 0 , 'Index for document not found' ) ;
477
+ indexTracker = indexTracker . delete ( change . doc . key ) ;
478
+ }
479
+ if ( change . type !== ChangeType . Removed ) {
480
+ indexTracker = indexTracker . add ( change . doc ) ;
481
+ newIndex = indexTracker . indexOf ( change . doc . key ) ;
482
+ }
483
+ return {
484
+ type : this . resultChangeType ( change . type ) ,
485
+ doc,
486
+ oldIndex,
487
+ newIndex
488
+ } ;
489
+ } ) ;
490
+ }
491
+ }
492
+
493
+ resultChangeType ( type : ChangeType ) : DocumentChangeType {
494
+ switch ( type ) {
495
+ case ChangeType . Added :
496
+ return 'added' ;
497
+ case ChangeType . Modified :
498
+ case ChangeType . Metadata :
499
+ return 'modified' ;
500
+ case ChangeType . Removed :
501
+ return 'removed' ;
502
+ default :
503
+ return fail ( 'Unknown change type: ' + type ) ;
504
+ }
505
+ }
506
+
417
507
private _convertToDocumentSnapshot (
418
508
doc : Document ,
419
509
fromCache : boolean ,
0 commit comments