Skip to content

Commit 5beb23c

Browse files
Index-Free Queries (feature, code still disabled) (#2180)
1 parent a308f0f commit 5beb23c

40 files changed

+2609
-819
lines changed

packages/firestore/src/core/firestore_client.ts

Lines changed: 17 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,7 @@ import { IndexedDbPersistence } from '../local/indexeddb_persistence';
2121
import { LocalStore } from '../local/local_store';
2222
import { MemoryPersistence } from '../local/memory_persistence';
2323
import { Persistence } from '../local/persistence';
24-
import {
25-
DocumentKeySet,
26-
documentKeySet,
27-
DocumentMap
28-
} from '../model/collections';
24+
import { SimpleQueryEngine } from '../local/simple_query_engine';
2925
import { Document, MaybeDocument, NoDocument } from '../model/document';
3026
import { DocumentKey } from '../model/document_key';
3127
import { Mutation } from '../model/mutation';
@@ -44,7 +40,7 @@ import {
4440
QueryListener
4541
} from './event_manager';
4642
import { SyncEngine } from './sync_engine';
47-
import { View, ViewDocumentChanges } from './view';
43+
import { View } from './view';
4844

4945
import {
5046
LruGarbageCollector,
@@ -401,7 +397,9 @@ export class FirestoreClient {
401397
return this.platform
402398
.loadConnection(this.databaseInfo)
403399
.then(async connection => {
404-
this.localStore = new LocalStore(this.persistence, user);
400+
// TODO(index-free): Use IndexFreeQueryEngine/IndexedQueryEngine as appropriate.
401+
const queryEngine = new SimpleQueryEngine();
402+
this.localStore = new LocalStore(this.persistence, queryEngine, user);
405403
if (maybeLruGc) {
406404
// We're running LRU Garbage collection. Set up the scheduler.
407405
this.lruScheduler = new LruScheduler(
@@ -581,22 +579,18 @@ export class FirestoreClient {
581579

582580
getDocumentsFromLocalCache(query: Query): Promise<ViewSnapshot> {
583581
this.verifyNotTerminated();
584-
return this.asyncQueue
585-
.enqueue(() => {
586-
return this.localStore.executeQuery(query);
587-
})
588-
.then((docs: DocumentMap) => {
589-
const remoteKeys: DocumentKeySet = documentKeySet();
590-
591-
const view = new View(query, remoteKeys);
592-
const viewDocChanges: ViewDocumentChanges = view.computeDocChanges(
593-
docs
594-
);
595-
return view.applyChanges(
596-
viewDocChanges,
597-
/* updateLimboDocuments= */ false
598-
).snapshot!;
599-
});
582+
return this.asyncQueue.enqueue(async () => {
583+
const queryResult = await this.localStore.executeQuery(
584+
query,
585+
/* usePreviousResults= */ true
586+
);
587+
const view = new View(query, queryResult.remoteKeys);
588+
const viewDocChanges = view.computeDocChanges(queryResult.documents);
589+
return view.applyChanges(
590+
viewDocChanges,
591+
/* updateLimboDocuments= */ false
592+
).snapshot!;
593+
});
600594
}
601595

602596
write(mutations: Mutation[]): Promise<void> {

packages/firestore/src/core/query.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,22 @@ export class Query {
199199
);
200200
}
201201

202+
/**
203+
* Returns true if this query does not specify any query constraints that
204+
* could remove results.
205+
*/
206+
matchesAllDocuments(): boolean {
207+
return (
208+
this.filters.length === 0 &&
209+
this.limit === null &&
210+
this.startAt == null &&
211+
this.endAt == null &&
212+
(this.explicitOrderBy.length === 0 ||
213+
(this.explicitOrderBy.length === 1 &&
214+
this.explicitOrderBy[0].field.isKeyField()))
215+
);
216+
}
217+
202218
// TODO(b/29183165): This is used to get a unique string from a query to, for
203219
// example, use as a dictionary key, but the implementation is subject to
204220
// collisions. Make it collision-free.

packages/firestore/src/core/sync_engine.ts

Lines changed: 27 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ import { Deferred } from '../util/promise';
4141
import { SortedMap } from '../util/sorted_map';
4242

4343
import { ignoreIfPrimaryLeaseLoss } from '../local/indexeddb_persistence';
44-
import { isDocumentChangeMissingError } from '../local/indexeddb_remote_document_cache';
4544
import { ClientId, SharedClientState } from '../local/shared_client_state';
4645
import {
4746
QueryTargetState,
@@ -244,13 +243,12 @@ export class SyncEngine implements RemoteSyncer, SharedClientStateSyncer {
244243
current: boolean
245244
): Promise<ViewSnapshot> {
246245
const query = queryData.query;
247-
const docs = await this.localStore.executeQuery(query);
248-
const remoteKeys = await this.localStore.remoteDocumentKeys(
249-
queryData.targetId
246+
const queryResult = await this.localStore.executeQuery(
247+
query,
248+
/* usePreviousResults= */ true
250249
);
251-
252-
const view = new View(query, remoteKeys);
253-
const viewDocChanges = view.computeDocChanges(docs);
250+
const view = new View(query, queryResult.remoteKeys);
251+
const viewDocChanges = view.computeDocChanges(queryResult.documents);
254252
const synthesizedTargetChange = TargetChange.createSynthesizedTargetChangeForCurrentChange(
255253
queryData.targetId,
256254
current && this.onlineState !== OnlineState.Offline
@@ -283,13 +281,12 @@ export class SyncEngine implements RemoteSyncer, SharedClientStateSyncer {
283281
private async synchronizeViewAndComputeSnapshot(
284282
queryView: QueryView
285283
): Promise<ViewChange> {
286-
const docs = await this.localStore.executeQuery(queryView.query);
287-
const remoteKeys = await this.localStore.remoteDocumentKeys(
288-
queryView.targetId
284+
const queryResult = await this.localStore.executeQuery(
285+
queryView.query,
286+
/* usePreviousResults= */ true
289287
);
290288
const viewSnapshot = queryView.view.synchronizeWithPersistedState(
291-
docs,
292-
remoteKeys
289+
queryResult
293290
);
294291
if (this.isPrimary) {
295292
this.updateTrackedLimbos(queryView.targetId, viewSnapshot.limboChanges);
@@ -787,9 +784,14 @@ export class SyncEngine implements RemoteSyncer, SharedClientStateSyncer {
787784
// The query has a limit and some docs were removed, so we need
788785
// to re-run the query against the local store to make sure we
789786
// didn't lose any good docs that had been past the limit.
790-
return this.localStore.executeQuery(queryView.query).then(docs => {
791-
return queryView.view.computeDocChanges(docs, viewDocChanges);
792-
});
787+
return this.localStore
788+
.executeQuery(queryView.query, /* usePreviousResults= */ false)
789+
.then(({ documents }) => {
790+
return queryView.view.computeDocChanges(
791+
documents,
792+
viewDocChanges
793+
);
794+
});
793795
})
794796
.then((viewDocChanges: ViewDocumentChanges) => {
795797
const targetChange =
@@ -983,29 +985,16 @@ export class SyncEngine implements RemoteSyncer, SharedClientStateSyncer {
983985
switch (state) {
984986
case 'current':
985987
case 'not-current': {
986-
try {
987-
const changes = await this.localStore.getNewDocumentChanges();
988-
const synthesizedRemoteEvent = RemoteEvent.createSynthesizedRemoteEventForCurrentChange(
989-
targetId,
990-
state === 'current'
991-
);
992-
await this.emitNewSnapsAndNotifyLocalStore(
993-
changes,
994-
synthesizedRemoteEvent
995-
);
996-
break;
997-
// Catch errors thrown by getNewDocumentchanges().
998-
} catch (error) {
999-
if (isDocumentChangeMissingError(error)) {
1000-
const activeTargets: TargetId[] = [];
1001-
objUtils.forEachNumber(this.queryViewsByTarget, target =>
1002-
activeTargets.push(target)
1003-
);
1004-
await this.synchronizeQueryViewsAndRaiseSnapshots(activeTargets);
1005-
} else {
1006-
throw error;
1007-
}
1008-
}
988+
const changes = await this.localStore.getNewDocumentChanges();
989+
const synthesizedRemoteEvent = RemoteEvent.createSynthesizedRemoteEventForCurrentChange(
990+
targetId,
991+
state === 'current'
992+
);
993+
await this.emitNewSnapsAndNotifyLocalStore(
994+
changes,
995+
synthesizedRemoteEvent
996+
);
997+
break;
1009998
}
1010999
case 'rejected': {
10111000
const queryView = this.queryViewsByTarget[targetId];

packages/firestore/src/core/view.ts

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
* limitations under the License.
1616
*/
1717

18+
import { QueryResult } from '../local/local_store';
1819
import {
1920
documentKeySet,
2021
DocumentKeySet,
@@ -433,21 +434,18 @@ export class View {
433434
* of `syncedDocuments` since secondary clients update their query views
434435
* based purely on synthesized RemoteEvents.
435436
*
436-
* @param localDocs - The documents that match the query according to the
437-
* LocalStore.
438-
* @param remoteKeys - The keys of the documents that match the query
439-
* according to the backend.
437+
* @param queryResult.documents - The documents that match the query according
438+
* to the LocalStore.
439+
* @param queryResult.remoteKeys - The keys of the documents that match the
440+
* query according to the backend.
440441
*
441442
* @return The ViewChange that resulted from this synchronization.
442443
*/
443444
// PORTING NOTE: Multi-tab only.
444-
synchronizeWithPersistedState(
445-
localDocs: MaybeDocumentMap,
446-
remoteKeys: DocumentKeySet
447-
): ViewChange {
448-
this._syncedDocuments = remoteKeys;
445+
synchronizeWithPersistedState(queryResult: QueryResult): ViewChange {
446+
this._syncedDocuments = queryResult.remoteKeys;
449447
this.limboDocuments = documentKeySet();
450-
const docChanges = this.computeDocChanges(localDocs);
448+
const docChanges = this.computeDocChanges(queryResult.documents);
451449
return this.applyChanges(docChanges, /*updateLimboDocuments=*/ true);
452450
}
453451

packages/firestore/src/core/view_snapshot.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,8 +168,8 @@ export class ViewSnapshot {
168168
changes,
169169
mutatedKeys,
170170
fromCache,
171-
true,
172-
false
171+
/* syncStateChanged= */ true,
172+
/* excludesMetadataChanges= */ false
173173
);
174174
}
175175

0 commit comments

Comments
 (0)