Skip to content

Commit 0778d94

Browse files
Update IndexFreeQueryEngine with changes from Web (#816)
1 parent e209092 commit 0778d94

File tree

1 file changed

+28
-36
lines changed

1 file changed

+28
-36
lines changed

firebase-firestore/src/main/java/com/google/firebase/firestore/local/IndexFreeQueryEngine.java

Lines changed: 28 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -28,27 +28,22 @@
2828
import java.util.Collections;
2929
import java.util.Map;
3030

31+
// TOOD(b/140938512): Drop SimpleQueryEngine and rename IndexFreeQueryEngine.
32+
3133
/**
3234
* A query engine that takes advantage of the target document mapping in the QueryCache. The
33-
* IndexFreeQueryEngine optimizes query execution by only reading the documents previously matched a
34-
* query plus any documents that were edited after the query was last listened to.
35+
* IndexFreeQueryEngine optimizes query execution by only reading the documents that previously
36+
* matched a query plus any documents that were edited after the query was last listened to.
3537
*
36-
* <p>There are some cases where Index-Free queries are not guaranteed to produce to the same
37-
* results as full collection scans. In these case, the IndexFreeQueryEngine falls back to a full
38-
* query processing. These cases are:
38+
* <p>There are some cases where Index-Free queries are not guaranteed to produce the same results
39+
* as full collection scans. In these cases, the IndexFreeQueryEngine falls back to full query
40+
* processing. These cases are:
3941
*
4042
* <ol>
41-
* <li>Limit queries where a document that matched the query previously no longer matches the
42-
* query. In this case, we have to scan all local documents since a document that was sent to
43-
* us as part of a different query result may now fall into the limit.
44-
* <li>Limit queries that include edits that occurred after the last remote snapshot (both
45-
* latency-compensated or committed). Even if an edited document continues to match the query,
46-
* an edit may cause a document to sort below another document that is in the local cache.
47-
* <li>Queries where the last snapshot contained Limbo documents. Even though a Limbo document is
48-
* not part of the backend result set, we need to include Limbo documents in local views to
49-
* ensure consistency between different Query views. If there exists a previous query snapshot
50-
* that contained no limbo documents, we can instead use the older snapshot version for
51-
* Index-Free processing.
43+
* <il>Limit queries where a document that matched the query previously no longer matches the
44+
* query. <il> Limit queries where a document edit may cause the document to sort below another
45+
* document that is in the local cache. <il>Queries that have never been CURRENT or free of Limbo
46+
* documents.
5247
* </ol>
5348
*/
5449
public class IndexFreeQueryEngine implements QueryEngine {
@@ -79,7 +74,9 @@ public ImmutableSortedMap<DocumentKey, Document> getDocumentsMatchingQuery(
7974
return executeFullCollectionScan(query);
8075
}
8176

82-
ImmutableSortedSet<Document> previousResults = getSortedPreviousResults(query, remoteKeys);
77+
ImmutableSortedMap<DocumentKey, MaybeDocument> documents =
78+
localDocumentsView.getDocuments(remoteKeys);
79+
ImmutableSortedSet<Document> previousResults = applyQuery(query, documents);
8380

8481
if (query.hasLimit()
8582
&& needsRefill(previousResults, remoteKeys, queryData.getLastLimboFreeSnapshotVersion())) {
@@ -99,35 +96,31 @@ && needsRefill(previousResults, remoteKeys, queryData.getLastLimboFreeSnapshotVe
9996
ImmutableSortedMap<DocumentKey, Document> updatedResults =
10097
localDocumentsView.getDocumentsMatchingQuery(
10198
query, queryData.getLastLimboFreeSnapshotVersion());
99+
100+
// We merge `previousResults` into `updateResults`, since `updateResults` is already a
101+
// ImmutableSortedMap. If a document is contained in both lists, then its contents are the same.
102102
for (Document result : previousResults) {
103103
updatedResults = updatedResults.insert(result.getKey(), result);
104104
}
105105

106106
return updatedResults;
107107
}
108108

109-
/**
110-
* Returns the documents for the specified remote keys if they still match the query, sorted by
111-
* the query's comparator.
112-
*/
113-
private ImmutableSortedSet<Document> getSortedPreviousResults(
114-
Query query, ImmutableSortedSet<DocumentKey> remoteKeys) {
115-
// Fetch the documents that matched the query at the last snapshot.
116-
ImmutableSortedMap<DocumentKey, MaybeDocument> previousResults =
117-
localDocumentsView.getDocuments(remoteKeys);
118-
109+
/** Applies the query filter and sorting to the provided documents. */
110+
private ImmutableSortedSet<Document> applyQuery(
111+
Query query, ImmutableSortedMap<DocumentKey, MaybeDocument> documents) {
119112
// Sort the documents and re-apply the query filter since previously matching documents do not
120113
// necessarily still match the query.
121-
ImmutableSortedSet<Document> results =
114+
ImmutableSortedSet<Document> queryResults =
122115
new ImmutableSortedSet<>(Collections.emptyList(), query.comparator());
123-
for (Map.Entry<DocumentKey, MaybeDocument> entry : previousResults) {
116+
for (Map.Entry<DocumentKey, MaybeDocument> entry : documents) {
124117
MaybeDocument maybeDoc = entry.getValue();
125118
if (maybeDoc instanceof Document && query.matches((Document) maybeDoc)) {
126119
Document doc = (Document) maybeDoc;
127-
results = results.insert(doc);
120+
queryResults = queryResults.insert(doc);
128121
}
129122
}
130-
return results;
123+
return queryResults;
131124
}
132125

133126
/**
@@ -149,11 +142,6 @@ private boolean needsRefill(
149142
return true;
150143
}
151144

152-
// We don't need to find a better match from cache if no documents matched the query.
153-
if (sortedPreviousResults.isEmpty()) {
154-
return false;
155-
}
156-
157145
// Limit queries are not eligible for index-free query execution if there is a potential that an
158146
// older document from cache now sorts before a document that was previously part of the limit.
159147
// This, however, can only happen if the last document of the limit sorts lower than it did when
@@ -162,6 +150,10 @@ private boolean needsRefill(
162150
// continue to be "rejected" by this boundary. Therefore, we can ignore any modifications that
163151
// don't affect the last document.
164152
Document lastDocumentInLimit = sortedPreviousResults.getMaxEntry();
153+
if (lastDocumentInLimit == null) {
154+
// We don't need to refill the query if there were already no documents.
155+
return false;
156+
}
165157
return lastDocumentInLimit.hasPendingWrites()
166158
|| lastDocumentInLimit.getVersion().compareTo(limboFreeSnapshotVersion) > 0;
167159
}

0 commit comments

Comments
 (0)