Skip to content

Commit e5abb85

Browse files
committed
add resumeToken to viewSnapshot and raiseInitialEvent condition check
1 parent b3951c6 commit e5abb85

File tree

6 files changed

+61
-19
lines changed

6 files changed

+61
-19
lines changed

packages/firestore/src/core/event_manager.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,8 @@ export class QueryListener {
307307
snap.mutatedKeys,
308308
snap.fromCache,
309309
snap.syncStateChanged,
310-
/* excludesMetadataChanges= */ true
310+
/* excludesMetadataChanges= */ true,
311+
snap.resumeToken
311312
);
312313
}
313314
let raisedEvent = false;
@@ -372,7 +373,11 @@ export class QueryListener {
372373
}
373374

374375
// Raise data from cache if we have any documents or we are offline
375-
return !snap.docs.isEmpty() || onlineState === OnlineState.Offline;
376+
return (
377+
!snap.docs.isEmpty() ||
378+
snap.resumeToken.approximateByteSize() > 0 ||
379+
onlineState === OnlineState.Offline
380+
);
376381
}
377382

378383
private shouldRaiseEvent(snap: ViewSnapshot): boolean {
@@ -405,7 +410,8 @@ export class QueryListener {
405410
snap.query,
406411
snap.docs,
407412
snap.mutatedKeys,
408-
snap.fromCache
413+
snap.fromCache,
414+
snap.resumeToken
409415
);
410416
this.raisedInitialEvent = true;
411417
this.queryObserver.next(snap);

packages/firestore/src/core/sync_engine_impl.ts

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ import {
6464
import { debugAssert, debugCast, fail, hardAssert } from '../util/assert';
6565
import { wrapInUserErrorIfRecoverable } from '../util/async_queue';
6666
import { BundleReader } from '../util/bundle_reader';
67+
import { ByteString } from '../util/byte_string';
6768
import { Code, FirestoreError } from '../util/error';
6869
import { logDebug, logWarn } from '../util/log';
6970
import { primitiveComparator } from '../util/misc';
@@ -327,7 +328,8 @@ export async function syncEngineListen(
327328
syncEngineImpl,
328329
query,
329330
targetId,
330-
status === 'current'
331+
status === 'current',
332+
targetData.resumeToken
331333
);
332334
}
333335

@@ -342,7 +344,8 @@ async function initializeViewAndComputeSnapshot(
342344
syncEngineImpl: SyncEngineImpl,
343345
query: Query,
344346
targetId: TargetId,
345-
current: boolean
347+
current: boolean,
348+
resumeToken: ByteString
346349
): Promise<ViewSnapshot> {
347350
// PORTING NOTE: On Web only, we inject the code that registers new Limbo
348351
// targets based on view changes. This allows us to only depend on Limbo
@@ -360,7 +363,8 @@ async function initializeViewAndComputeSnapshot(
360363
const synthesizedTargetChange =
361364
TargetChange.createSynthesizedTargetChangeForCurrentChange(
362365
targetId,
363-
current && syncEngineImpl.onlineState !== OnlineState.Offline
366+
current && syncEngineImpl.onlineState !== OnlineState.Offline,
367+
resumeToken
364368
);
365369
const viewChange = view.applyChanges(
366370
viewDocChanges,
@@ -1379,7 +1383,8 @@ async function synchronizeQueryViewsAndRaiseSnapshots(
13791383
syncEngineImpl,
13801384
synthesizeTargetToQuery(target!),
13811385
targetId,
1382-
/*current=*/ false
1386+
/*current=*/ false,
1387+
targetData.resumeToken
13831388
);
13841389
}
13851390

@@ -1451,7 +1456,8 @@ export async function syncEngineApplyTargetState(
14511456
const synthesizedRemoteEvent =
14521457
RemoteEvent.createSynthesizedRemoteEventForCurrentChange(
14531458
targetId,
1454-
state === 'current'
1459+
state === 'current',
1460+
ByteString.EMPTY_BYTE_STRING
14551461
);
14561462
await syncEngineEmitNewSnapsAndNotifyLocalStore(
14571463
syncEngineImpl,
@@ -1506,7 +1512,8 @@ export async function syncEngineApplyActiveTargetsChange(
15061512
syncEngineImpl,
15071513
synthesizeTargetToQuery(target),
15081514
targetData.targetId,
1509-
/*current=*/ false
1515+
/*current=*/ false,
1516+
targetData.resumeToken
15101517
);
15111518
remoteStoreListen(syncEngineImpl.remoteStore, targetData);
15121519
}

packages/firestore/src/core/view.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import { DocumentKey } from '../model/document_key';
2626
import { DocumentSet } from '../model/document_set';
2727
import { TargetChange } from '../remote/remote_event';
2828
import { debugAssert, fail } from '../util/assert';
29+
import { ByteString } from '../util/byte_string';
2930

3031
import { LimitType, newQueryComparator, Query, queryMatches } from './query';
3132
import { OnlineState } from './types';
@@ -72,6 +73,7 @@ export interface ViewChange {
7273
*/
7374
export class View {
7475
private syncState: SyncState | null = null;
76+
private resumeToken: ByteString | null = null;
7577
/**
7678
* A flag whether the view is current with the backend. A view is considered
7779
* current after it has seen the current flag from the backend and did not
@@ -319,7 +321,8 @@ export class View {
319321
docChanges.mutatedKeys,
320322
newSyncState === SyncState.Local,
321323
syncStateChanged,
322-
/* excludesMetadataChanges= */ false
324+
/* excludesMetadataChanges= */ false,
325+
targetChange?.resumeToken ?? ByteString.EMPTY_BYTE_STRING
323326
);
324327
return {
325328
snapshot: snap,
@@ -468,7 +471,8 @@ export class View {
468471
this.query,
469472
this.documentSet,
470473
this.mutatedKeys,
471-
this.syncState === SyncState.Local
474+
this.syncState === SyncState.Local,
475+
this.resumeToken ?? ByteString.EMPTY_BYTE_STRING
472476
);
473477
}
474478
}

packages/firestore/src/core/view_snapshot.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { Document } from '../model/document';
2020
import { DocumentKey } from '../model/document_key';
2121
import { DocumentSet } from '../model/document_set';
2222
import { fail } from '../util/assert';
23+
import { ByteString } from '../util/byte_string';
2324
import { SortedMap } from '../util/sorted_map';
2425

2526
import { Query, queryEquals } from './query';
@@ -146,15 +147,17 @@ export class ViewSnapshot {
146147
readonly mutatedKeys: DocumentKeySet,
147148
readonly fromCache: boolean,
148149
readonly syncStateChanged: boolean,
149-
readonly excludesMetadataChanges: boolean
150+
readonly excludesMetadataChanges: boolean,
151+
readonly resumeToken: ByteString
150152
) {}
151153

152154
/** Returns a view snapshot as if all documents in the snapshot were added. */
153155
static fromInitialDocuments(
154156
query: Query,
155157
documents: DocumentSet,
156158
mutatedKeys: DocumentKeySet,
157-
fromCache: boolean
159+
fromCache: boolean,
160+
resumeToken: ByteString
158161
): ViewSnapshot {
159162
const changes: DocumentViewChange[] = [];
160163
documents.forEach(doc => {
@@ -169,7 +172,8 @@ export class ViewSnapshot {
169172
mutatedKeys,
170173
fromCache,
171174
/* syncStateChanged= */ true,
172-
/* excludesMetadataChanges= */ false
175+
/* excludesMetadataChanges= */ false,
176+
resumeToken
173177
);
174178
}
175179

@@ -184,7 +188,8 @@ export class ViewSnapshot {
184188
!this.mutatedKeys.isEqual(other.mutatedKeys) ||
185189
!queryEquals(this.query, other.query) ||
186190
!this.docs.isEqual(other.docs) ||
187-
!this.oldDocs.isEqual(other.oldDocs)
191+
!this.oldDocs.isEqual(other.oldDocs) ||
192+
this.resumeToken !== other.resumeToken
188193
) {
189194
return false;
190195
}

packages/firestore/src/remote/remote_event.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,14 +67,16 @@ export class RemoteEvent {
6767
// PORTING NOTE: Multi-tab only
6868
static createSynthesizedRemoteEventForCurrentChange(
6969
targetId: TargetId,
70-
current: boolean
70+
current: boolean,
71+
resumeToken: ByteString
7172
): RemoteEvent {
7273
const targetChanges = new Map<TargetId, TargetChange>();
7374
targetChanges.set(
7475
targetId,
7576
TargetChange.createSynthesizedTargetChangeForCurrentChange(
7677
targetId,
77-
current
78+
current,
79+
resumeToken
7880
)
7981
);
8082
return new RemoteEvent(
@@ -134,10 +136,11 @@ export class TargetChange {
134136
*/
135137
static createSynthesizedTargetChangeForCurrentChange(
136138
targetId: TargetId,
137-
current: boolean
139+
current: boolean,
140+
resumeToken: ByteString
138141
): TargetChange {
139142
return new TargetChange(
140-
ByteString.EMPTY_BYTE_STRING,
143+
resumeToken,
141144
current,
142145
documentKeySet(),
143146
documentKeySet(),

packages/firestore/test/integration/api/query.test.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1288,6 +1288,23 @@ apiDescribe('Queries', (persistence: boolean) => {
12881288
expect(toDataArray(snapshot)).to.deep.equal([{ map: { nested: 'foo' } }]);
12891289
});
12901290
});
1291+
1292+
// eslint-disable-next-line no-restricted-properties
1293+
(persistence ? it.only : it.skip)('empty query results are cached', () => {
1294+
// Reproduces https://github.com/firebase/firebase-js-sdk/issues/5873
1295+
return withTestCollection(persistence, {}, async coll => {
1296+
const snapshot1 = await getDocs(coll); // Populate the cache
1297+
expect(snapshot1.metadata.fromCache).to.be.false;
1298+
expect(toDataArray(snapshot1)).to.deep.equal([]); // Precondition check
1299+
1300+
// Add a snapshot listener whose first event should be raised from cache.
1301+
const storeEvent = new EventsAccumulator<QuerySnapshot>();
1302+
onSnapshot(coll, storeEvent.storeEvent);
1303+
const snapshot2 = await storeEvent.awaitEvent();
1304+
expect(snapshot2.metadata.fromCache).to.be.true;
1305+
expect(toDataArray(snapshot2)).to.deep.equal([]);
1306+
});
1307+
});
12911308
});
12921309

12931310
function verifyDocumentChange<T>(

0 commit comments

Comments
 (0)