Skip to content

Commit 5ba4c26

Browse files
Compat class for QuerySnapshot (#4058)
1 parent f69db37 commit 5ba4c26

File tree

5 files changed

+233
-324
lines changed

5 files changed

+233
-324
lines changed

packages/firestore/exp/src/api/snapshot.ts

Lines changed: 109 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,14 @@ import {
2929
queryEqual,
3030
SetOptions
3131
} from '../../../lite/src/api/reference';
32-
import {
33-
changesFromSnapshot,
34-
SnapshotMetadata
35-
} from '../../../src/api/database';
32+
import { SnapshotMetadata } from '../../../src/api/database';
3633
import { Code, FirestoreError } from '../../../src/util/error';
37-
import { ViewSnapshot } from '../../../src/core/view_snapshot';
34+
import { ChangeType, ViewSnapshot } from '../../../src/core/view_snapshot';
3835
import { FieldPath } from '../../../lite/src/api/field_path';
3936
import { SnapshotListenOptions } from './reference';
4037
import { UntypedFirestoreDataConverter } from '../../../src/api/user_data_reader';
38+
import { debugAssert, fail } from '../../../src/util/assert';
39+
import { newQueryComparator } from '../../../src/core/query';
4140

4241
/**
4342
* Converter used by `withConverter()` to transform user objects of type `T`
@@ -370,10 +369,16 @@ export class QuerySnapshot<T = DocumentData> {
370369
this._snapshot.docs.forEach(doc => {
371370
callback.call(
372371
thisArg,
373-
this._convertToDocumentSnapshot(
372+
new QueryDocumentSnapshot<T>(
373+
this._firestore,
374+
this._userDataWriter,
375+
doc.key,
374376
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
377382
)
378383
);
379384
});
@@ -403,30 +408,108 @@ export class QuerySnapshot<T = DocumentData> {
403408
!this._cachedChanges ||
404409
this._cachedChangesIncludeMetadataChanges !== includeMetadataChanges
405410
) {
406-
this._cachedChanges = changesFromSnapshot<QueryDocumentSnapshot<T>>(
407-
this._snapshot,
408-
includeMetadataChanges,
409-
this._convertToDocumentSnapshot.bind(this)
410-
);
411+
this._cachedChanges = changesFromSnapshot(this, includeMetadataChanges);
411412
this._cachedChangesIncludeMetadataChanges = includeMetadataChanges;
412413
}
413414

414415
return this._cachedChanges;
415416
}
417+
}
416418

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);
430513
}
431514
}
432515

packages/firestore/exp/test/shim.ts

Lines changed: 1 addition & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ import {
2929
query,
3030
queryEqual,
3131
refEqual,
32-
snapshotEqual,
3332
endAt,
3433
endBefore,
3534
startAfter,
@@ -50,7 +49,7 @@ import {
5049
Firestore,
5150
DocumentReference,
5251
DocumentSnapshot,
53-
QueryDocumentSnapshot,
52+
QuerySnapshot,
5453
wrapObserver,
5554
extractSnapshotOptions
5655
} from '../../src/api/database';
@@ -309,62 +308,6 @@ export class Query<T = legacy.DocumentData>
309308
}
310309
}
311310

312-
export class QuerySnapshot<T = legacy.DocumentData>
313-
implements legacy.QuerySnapshot<T> {
314-
constructor(
315-
readonly _firestore: Firestore,
316-
readonly _delegate: exp.QuerySnapshot<T>
317-
) {}
318-
319-
readonly query = new Query(this._firestore, this._delegate.query);
320-
readonly metadata = this._delegate.metadata;
321-
readonly size = this._delegate.size;
322-
readonly empty = this._delegate.empty;
323-
324-
get docs(): Array<QueryDocumentSnapshot<T>> {
325-
return this._delegate.docs.map(
326-
doc => new QueryDocumentSnapshot<T>(this._firestore, doc)
327-
);
328-
}
329-
330-
docChanges(options?: legacy.SnapshotListenOptions): Array<DocumentChange<T>> {
331-
return this._delegate
332-
.docChanges(options)
333-
.map(docChange => new DocumentChange<T>(this._firestore, docChange));
334-
}
335-
336-
forEach(
337-
callback: (result: QueryDocumentSnapshot<T>) => void,
338-
thisArg?: any
339-
): void {
340-
this._delegate.forEach(snapshot => {
341-
callback.call(
342-
thisArg,
343-
new QueryDocumentSnapshot(this._firestore, snapshot)
344-
);
345-
});
346-
}
347-
348-
isEqual(other: QuerySnapshot<T>): boolean {
349-
return snapshotEqual(this._delegate, other._delegate);
350-
}
351-
}
352-
353-
export class DocumentChange<T = legacy.DocumentData>
354-
implements legacy.DocumentChange<T> {
355-
constructor(
356-
private readonly _firestore: Firestore,
357-
private readonly _delegate: exp.DocumentChange<T>
358-
) {}
359-
readonly type = this._delegate.type;
360-
readonly doc = new QueryDocumentSnapshot<T>(
361-
this._firestore,
362-
this._delegate.doc
363-
);
364-
readonly oldIndex = this._delegate.oldIndex;
365-
readonly newIndex = this._delegate.oldIndex;
366-
}
367-
368311
export class CollectionReference<T = legacy.DocumentData>
369312
extends Query<T>
370313
implements legacy.CollectionReference<T> {

0 commit comments

Comments
 (0)