28
28
import java .util .Collections ;
29
29
import java .util .Map ;
30
30
31
+ // TOOD(b/140938512): Drop SimpleQueryEngine and rename IndexFreeQueryEngine.
32
+
31
33
/**
32
34
* 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.
35
37
*
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:
39
41
*
40
42
* <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.
52
47
* </ol>
53
48
*/
54
49
public class IndexFreeQueryEngine implements QueryEngine {
@@ -79,7 +74,9 @@ public ImmutableSortedMap<DocumentKey, Document> getDocumentsMatchingQuery(
79
74
return executeFullCollectionScan (query );
80
75
}
81
76
82
- ImmutableSortedSet <Document > previousResults = getSortedPreviousResults (query , remoteKeys );
77
+ ImmutableSortedMap <DocumentKey , MaybeDocument > documents =
78
+ localDocumentsView .getDocuments (remoteKeys );
79
+ ImmutableSortedSet <Document > previousResults = applyQuery (query , documents );
83
80
84
81
if (query .hasLimit ()
85
82
&& needsRefill (previousResults , remoteKeys , queryData .getLastLimboFreeSnapshotVersion ())) {
@@ -99,35 +96,31 @@ && needsRefill(previousResults, remoteKeys, queryData.getLastLimboFreeSnapshotVe
99
96
ImmutableSortedMap <DocumentKey , Document > updatedResults =
100
97
localDocumentsView .getDocumentsMatchingQuery (
101
98
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.
102
102
for (Document result : previousResults ) {
103
103
updatedResults = updatedResults .insert (result .getKey (), result );
104
104
}
105
105
106
106
return updatedResults ;
107
107
}
108
108
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 ) {
119
112
// Sort the documents and re-apply the query filter since previously matching documents do not
120
113
// necessarily still match the query.
121
- ImmutableSortedSet <Document > results =
114
+ ImmutableSortedSet <Document > queryResults =
122
115
new ImmutableSortedSet <>(Collections .emptyList (), query .comparator ());
123
- for (Map .Entry <DocumentKey , MaybeDocument > entry : previousResults ) {
116
+ for (Map .Entry <DocumentKey , MaybeDocument > entry : documents ) {
124
117
MaybeDocument maybeDoc = entry .getValue ();
125
118
if (maybeDoc instanceof Document && query .matches ((Document ) maybeDoc )) {
126
119
Document doc = (Document ) maybeDoc ;
127
- results = results .insert (doc );
120
+ queryResults = queryResults .insert (doc );
128
121
}
129
122
}
130
- return results ;
123
+ return queryResults ;
131
124
}
132
125
133
126
/**
@@ -149,11 +142,6 @@ private boolean needsRefill(
149
142
return true ;
150
143
}
151
144
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
-
157
145
// Limit queries are not eligible for index-free query execution if there is a potential that an
158
146
// older document from cache now sorts before a document that was previously part of the limit.
159
147
// 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(
162
150
// continue to be "rejected" by this boundary. Therefore, we can ignore any modifications that
163
151
// don't affect the last document.
164
152
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
+ }
165
157
return lastDocumentInLimit .hasPendingWrites ()
166
158
|| lastDocumentInLimit .getVersion ().compareTo (limboFreeSnapshotVersion ) > 0 ;
167
159
}
0 commit comments