@@ -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`
@@ -370,10 +369,16 @@ export class QuerySnapshot<T = DocumentData> {
370
369
this . _snapshot . docs . forEach ( doc => {
371
370
callback . call (
372
371
thisArg ,
373
- this . _convertToDocumentSnapshot (
372
+ new QueryDocumentSnapshot < T > (
373
+ this . _firestore ,
374
+ this . _userDataWriter ,
375
+ doc . key ,
374
376
doc ,
375
- this . _snapshot . fromCache ,
376
- this . _snapshot . mutatedKeys . has ( doc . key )
377
+ new SnapshotMetadata (
378
+ this . _snapshot . mutatedKeys . has ( doc . key ) ,
379
+ this . _snapshot . fromCache
380
+ ) ,
381
+ this . query . _converter
377
382
)
378
383
) ;
379
384
} ) ;
@@ -403,30 +408,108 @@ export class QuerySnapshot<T = DocumentData> {
403
408
! this . _cachedChanges ||
404
409
this . _cachedChangesIncludeMetadataChanges !== includeMetadataChanges
405
410
) {
406
- this . _cachedChanges = changesFromSnapshot < QueryDocumentSnapshot < T > > (
407
- this . _snapshot ,
408
- includeMetadataChanges ,
409
- this . _convertToDocumentSnapshot . bind ( this )
410
- ) ;
411
+ this . _cachedChanges = changesFromSnapshot ( this , includeMetadataChanges ) ;
411
412
this . _cachedChangesIncludeMetadataChanges = includeMetadataChanges ;
412
413
}
413
414
414
415
return this . _cachedChanges ;
415
416
}
417
+ }
416
418
417
- private _convertToDocumentSnapshot (
418
- doc : Document ,
419
- fromCache : boolean ,
420
- hasPendingWrites : boolean
421
- ) : QueryDocumentSnapshot < T > {
422
- return new QueryDocumentSnapshot < T > (
423
- this . _firestore ,
424
- this . _userDataWriter ,
425
- doc . key ,
426
- doc ,
427
- new SnapshotMetadata ( hasPendingWrites , fromCache ) ,
428
- this . query . _converter
429
- ) ;
419
+ /** Calculates the array of DocumentChanges for a given ViewSnapshot. */
420
+ export function changesFromSnapshot < T > (
421
+ querySnapshot : QuerySnapshot < T > ,
422
+ includeMetadataChanges : boolean
423
+ ) : Array < DocumentChange < T > > {
424
+ if ( querySnapshot . _snapshot . oldDocs . isEmpty ( ) ) {
425
+ // Special case the first snapshot because index calculation is easy and
426
+ // fast
427
+ let lastDoc : Document ;
428
+ let index = 0 ;
429
+ return querySnapshot . _snapshot . docChanges . map ( change => {
430
+ debugAssert (
431
+ change . type === ChangeType . Added ,
432
+ 'Invalid event type for first snapshot'
433
+ ) ;
434
+ debugAssert (
435
+ ! lastDoc ||
436
+ newQueryComparator ( querySnapshot . _snapshot . query ) (
437
+ lastDoc ,
438
+ change . doc
439
+ ) < 0 ,
440
+ 'Got added events in wrong order'
441
+ ) ;
442
+ const doc = new QueryDocumentSnapshot < T > (
443
+ querySnapshot . _firestore ,
444
+ querySnapshot . _userDataWriter ,
445
+ change . doc . key ,
446
+ change . doc ,
447
+ new SnapshotMetadata (
448
+ querySnapshot . _snapshot . mutatedKeys . has ( change . doc . key ) ,
449
+ querySnapshot . _snapshot . fromCache
450
+ ) ,
451
+ querySnapshot . query . _converter
452
+ ) ;
453
+ lastDoc = change . doc ;
454
+ return {
455
+ type : 'added' as DocumentChangeType ,
456
+ doc,
457
+ oldIndex : - 1 ,
458
+ newIndex : index ++
459
+ } ;
460
+ } ) ;
461
+ } else {
462
+ // A DocumentSet that is updated incrementally as changes are applied to use
463
+ // to lookup the index of a document.
464
+ let indexTracker = querySnapshot . _snapshot . oldDocs ;
465
+ return querySnapshot . _snapshot . docChanges
466
+ . filter (
467
+ change => includeMetadataChanges || change . type !== ChangeType . Metadata
468
+ )
469
+ . map ( change => {
470
+ const doc = new QueryDocumentSnapshot < T > (
471
+ querySnapshot . _firestore ,
472
+ querySnapshot . _userDataWriter ,
473
+ change . doc . key ,
474
+ change . doc ,
475
+ new SnapshotMetadata (
476
+ querySnapshot . _snapshot . mutatedKeys . has ( change . doc . key ) ,
477
+ querySnapshot . _snapshot . fromCache
478
+ ) ,
479
+ querySnapshot . query . _converter
480
+ ) ;
481
+ let oldIndex = - 1 ;
482
+ let newIndex = - 1 ;
483
+ if ( change . type !== ChangeType . Added ) {
484
+ oldIndex = indexTracker . indexOf ( change . doc . key ) ;
485
+ debugAssert ( oldIndex >= 0 , 'Index for document not found' ) ;
486
+ indexTracker = indexTracker . delete ( change . doc . key ) ;
487
+ }
488
+ if ( change . type !== ChangeType . Removed ) {
489
+ indexTracker = indexTracker . add ( change . doc ) ;
490
+ newIndex = indexTracker . indexOf ( change . doc . key ) ;
491
+ }
492
+ return {
493
+ type : resultChangeType ( change . type ) ,
494
+ doc,
495
+ oldIndex,
496
+ newIndex
497
+ } ;
498
+ } ) ;
499
+ }
500
+ }
501
+
502
+ export function resultChangeType ( type : ChangeType ) : DocumentChangeType {
503
+ switch ( type ) {
504
+ case ChangeType . Added :
505
+ return 'added' ;
506
+ case ChangeType . Modified :
507
+ case ChangeType . Metadata :
508
+ return 'modified' ;
509
+ case ChangeType . Removed :
510
+ return 'removed' ;
511
+ default :
512
+ return fail ( 'Unknown change type: ' + type ) ;
430
513
}
431
514
}
432
515
0 commit comments