Skip to content

Commit d43f0d0

Browse files
Compat class for QuerySnapshot
1 parent d2adf4e commit d43f0d0

File tree

4 files changed

+209
-280
lines changed

4 files changed

+209
-280
lines changed

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

Lines changed: 98 additions & 8 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`
@@ -403,17 +402,108 @@ export class QuerySnapshot<T = DocumentData> {
403402
!this._cachedChanges ||
404403
this._cachedChangesIncludeMetadataChanges !== includeMetadataChanges
405404
) {
406-
this._cachedChanges = changesFromSnapshot<QueryDocumentSnapshot<T>>(
405+
this._cachedChanges = this.changesFromSnapshot(
407406
this._snapshot,
408-
includeMetadataChanges,
409-
this._convertToDocumentSnapshot.bind(this)
407+
includeMetadataChanges
410408
);
411409
this._cachedChangesIncludeMetadataChanges = includeMetadataChanges;
412410
}
413411

414412
return this._cachedChanges;
415413
}
416414

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+
417507
private _convertToDocumentSnapshot(
418508
doc: Document,
419509
fromCache: boolean,

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)