Skip to content

Commit 0167358

Browse files
Filter document queries by update time
1 parent cefbaa2 commit 0167358

File tree

10 files changed

+76
-28
lines changed

10 files changed

+76
-28
lines changed

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

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import com.google.firebase.firestore.model.DocumentKey;
3030
import com.google.firebase.firestore.model.FieldPath;
3131
import com.google.firebase.firestore.model.MaybeDocument;
32+
import com.google.firebase.firestore.model.SnapshotVersion;
3233
import com.google.firebase.firestore.model.value.ArrayValue;
3334
import com.google.firebase.firestore.model.value.BooleanValue;
3435
import com.google.firebase.firestore.model.value.DoubleValue;
@@ -97,9 +98,10 @@ public IndexedQueryEngine(
9798
}
9899

99100
@Override
100-
public ImmutableSortedMap<DocumentKey, Document> getDocumentsMatchingQuery(Query query) {
101+
public ImmutableSortedMap<DocumentKey, Document> getDocumentsMatchingQuery(
102+
Query query, @Nullable QueryData queryData) {
101103
return query.isDocumentQuery()
102-
? localDocuments.getDocumentsMatchingQuery(query)
104+
? localDocuments.getDocumentsMatchingQuery(query, SnapshotVersion.NONE)
103105
: performCollectionQuery(query);
104106
}
105107

@@ -118,7 +120,7 @@ private ImmutableSortedMap<DocumentKey, Document> performCollectionQuery(Query q
118120
"If there are any filters, we should be able to use an index.");
119121
// TODO: Call overlay.getCollectionDocuments(query.getPath()) and filter the
120122
// results (there may still be startAt/endAt bounds that apply).
121-
filteredResults = localDocuments.getDocumentsMatchingQuery(query);
123+
filteredResults = localDocuments.getDocumentsMatchingQuery(query, SnapshotVersion.NONE);
122124
}
123125

124126
return filteredResults;

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

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -129,15 +129,22 @@ ImmutableSortedMap<DocumentKey, MaybeDocument> getLocalViewOfDocuments(
129129
// documents in a given collection so that SimpleQueryEngine can do that and then filter in
130130
// memory.
131131

132-
/** Performs a query against the local view of all documents. */
133-
ImmutableSortedMap<DocumentKey, Document> getDocumentsMatchingQuery(Query query) {
132+
/**
133+
* Performs a query against the local view of all documents.
134+
*
135+
* @param query The query to match documents against.
136+
* @param sinceUpdateTime If not set to SnapshotVersion.MIN, return only documents that have been
137+
* modified since this snapshot version (exclusive).
138+
*/
139+
ImmutableSortedMap<DocumentKey, Document> getDocumentsMatchingQuery(
140+
Query query, SnapshotVersion sinceUpdateTime) {
134141
ResourcePath path = query.getPath();
135142
if (query.isDocumentQuery()) {
136143
return getDocumentsMatchingDocumentQuery(path);
137144
} else if (query.isCollectionGroupQuery()) {
138-
return getDocumentsMatchingCollectionGroupQuery(query);
145+
return getDocumentsMatchingCollectionGroupQuery(query, sinceUpdateTime);
139146
} else {
140-
return getDocumentsMatchingCollectionQuery(query);
147+
return getDocumentsMatchingCollectionQuery(query, sinceUpdateTime);
141148
}
142149
}
143150

@@ -154,7 +161,7 @@ private ImmutableSortedMap<DocumentKey, Document> getDocumentsMatchingDocumentQu
154161
}
155162

156163
private ImmutableSortedMap<DocumentKey, Document> getDocumentsMatchingCollectionGroupQuery(
157-
Query query) {
164+
Query query, SnapshotVersion sinceUpdateTime) {
158165
hardAssert(
159166
query.getPath().isEmpty(),
160167
"Currently we only support collection group queries at the root.");
@@ -167,7 +174,7 @@ private ImmutableSortedMap<DocumentKey, Document> getDocumentsMatchingCollection
167174
for (ResourcePath parent : parents) {
168175
Query collectionQuery = query.asCollectionQueryAtPath(parent.append(collectionId));
169176
ImmutableSortedMap<DocumentKey, Document> collectionResults =
170-
getDocumentsMatchingCollectionQuery(collectionQuery);
177+
getDocumentsMatchingCollectionQuery(collectionQuery, sinceUpdateTime);
171178
for (Map.Entry<DocumentKey, Document> docEntry : collectionResults) {
172179
results = results.insert(docEntry.getKey(), docEntry.getValue());
173180
}
@@ -177,9 +184,9 @@ private ImmutableSortedMap<DocumentKey, Document> getDocumentsMatchingCollection
177184

178185
/** Queries the remote documents and overlays mutations. */
179186
private ImmutableSortedMap<DocumentKey, Document> getDocumentsMatchingCollectionQuery(
180-
Query query) {
187+
Query query, SnapshotVersion sinceUpdateTime) {
181188
ImmutableSortedMap<DocumentKey, Document> results =
182-
remoteDocumentCache.getAllDocumentsMatchingQuery(query);
189+
remoteDocumentCache.getAllDocumentsMatchingQuery(query, sinceUpdateTime);
183190

184191
List<MutationBatch> matchingBatches = mutationQueue.getAllMutationBatchesAffectingQuery(query);
185192
for (MutationBatch batch : matchingBatches) {

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -614,7 +614,12 @@ public void markNeedsRefill(int targetId) {
614614

615615
/** Runs the given query against all the documents in the local store and returns the results. */
616616
public ImmutableSortedMap<DocumentKey, Document> executeQuery(Query query) {
617-
return queryEngine.getDocumentsMatchingQuery(query);
617+
QueryData cachedQueryData = queryCache.getQueryData(query);
618+
QueryData updatedQueryData =
619+
cachedQueryData != null ? targetIds.get(cachedQueryData.getTargetId()) : null;
620+
621+
return queryEngine.getDocumentsMatchingQuery(
622+
query, updatedQueryData != null ? updatedQueryData : cachedQueryData);
618623
}
619624

620625
/**

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import com.google.firebase.firestore.model.DocumentKey;
2525
import com.google.firebase.firestore.model.MaybeDocument;
2626
import com.google.firebase.firestore.model.ResourcePath;
27+
import com.google.firebase.firestore.model.SnapshotVersion;
2728
import java.util.HashMap;
2829
import java.util.Iterator;
2930
import java.util.Map;
@@ -79,7 +80,8 @@ public Map<DocumentKey, MaybeDocument> getAll(Iterable<DocumentKey> keys) {
7980
}
8081

8182
@Override
82-
public ImmutableSortedMap<DocumentKey, Document> getAllDocumentsMatchingQuery(Query query) {
83+
public ImmutableSortedMap<DocumentKey, Document> getAllDocumentsMatchingQuery(
84+
Query query, SnapshotVersion sinceUpdateTime) {
8385
hardAssert(
8486
!query.isCollectionGroupQuery(),
8587
"CollectionGroup queries should be handled in LocalDocumentsView");
@@ -109,7 +111,7 @@ public ImmutableSortedMap<DocumentKey, Document> getAllDocumentsMatchingQuery(Qu
109111
}
110112

111113
Document doc = (Document) maybeDoc;
112-
if (query.matches(doc)) {
114+
if (doc.getVersion().compareTo(sinceUpdateTime) > 0 && query.matches(doc)) {
113115
result = result.insert(doc.getKey(), doc);
114116
}
115117
}

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,14 @@
1919
import com.google.firebase.firestore.model.Document;
2020
import com.google.firebase.firestore.model.DocumentKey;
2121
import com.google.firebase.firestore.model.MaybeDocument;
22+
import javax.annotation.Nullable;
2223

2324
/** Represents a query engine capable of performing queries over the local document cache. */
2425
interface QueryEngine {
2526

2627
/** Returns all local documents matching the specified query. */
27-
ImmutableSortedMap<DocumentKey, Document> getDocumentsMatchingQuery(Query query);
28+
ImmutableSortedMap<DocumentKey, Document> getDocumentsMatchingQuery(
29+
Query query, @Nullable QueryData queryData);
2830

2931
/**
3032
* Notifies the query engine of a document change in case it would like to update indexes and the

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import com.google.firebase.firestore.model.Document;
2020
import com.google.firebase.firestore.model.DocumentKey;
2121
import com.google.firebase.firestore.model.MaybeDocument;
22+
import com.google.firebase.firestore.model.SnapshotVersion;
2223
import java.util.Map;
2324
import javax.annotation.Nullable;
2425

@@ -73,7 +74,10 @@ interface RemoteDocumentCache {
7374
* <p>Cached NoDocument entries have no bearing on query results.
7475
*
7576
* @param query The query to match documents against.
77+
* @param sinceUpdateTime If not set to SnapshotVersion.MIN, return only documents that have been
78+
* modified since this snapshot version (exclusive).
7679
* @return The set of matching documents.
7780
*/
78-
ImmutableSortedMap<DocumentKey, Document> getAllDocumentsMatchingQuery(Query query);
81+
ImmutableSortedMap<DocumentKey, Document> getAllDocumentsMatchingQuery(
82+
Query query, SnapshotVersion sinceUpdateTime);
7983
}

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

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,8 @@ public Map<DocumentKey, MaybeDocument> getAll(Iterable<DocumentKey> documentKeys
128128
}
129129

130130
@Override
131-
public ImmutableSortedMap<DocumentKey, Document> getAllDocumentsMatchingQuery(Query query) {
131+
public ImmutableSortedMap<DocumentKey, Document> getAllDocumentsMatchingQuery(
132+
Query query, SnapshotVersion sinceUpdateTime) {
132133
hardAssert(
133134
!query.isCollectionGroupQuery(),
134135
"CollectionGroup queries should be handled in LocalDocumentsView");
@@ -147,8 +148,10 @@ public ImmutableSortedMap<DocumentKey, Document> getAllDocumentsMatchingQuery(Qu
147148
new ImmutableSortedMap[] {DocumentCollections.emptyDocumentMap()};
148149

149150
int rowsProcessed =
150-
db.query("SELECT path, contents FROM remote_documents WHERE path >= ? AND path < ?")
151-
.binding(prefixPath, prefixSuccessorPath)
151+
db.query(
152+
"SELECT path, contents FROM remote_documents WHERE path >= ? AND path < ? "
153+
+ "AND (snapshot_version_micros > ? OR snapshot_version_micros IS NULL)")
154+
.binding(prefixPath, prefixSuccessorPath, sinceUpdateTime.toMicroseconds())
152155
.forEach(
153156
row -> {
154157
// TODO: Actually implement a single-collection query

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
import com.google.firebase.firestore.model.Document;
2020
import com.google.firebase.firestore.model.DocumentKey;
2121
import com.google.firebase.firestore.model.MaybeDocument;
22+
import com.google.firebase.firestore.model.SnapshotVersion;
23+
import javax.annotation.Nullable;
2224

2325
/**
2426
* A naive implementation of QueryEngine that just loads all the documents in the queried collection
@@ -33,10 +35,11 @@ public SimpleQueryEngine(LocalDocumentsView localDocumentsView) {
3335
}
3436

3537
@Override
36-
public ImmutableSortedMap<DocumentKey, Document> getDocumentsMatchingQuery(Query query) {
38+
public ImmutableSortedMap<DocumentKey, Document> getDocumentsMatchingQuery(
39+
Query query, @Nullable QueryData queryData) {
3740
// TODO: Once LocalDocumentsView provides a getCollectionDocuments() method, we
3841
// should call that here and then filter the results.
39-
return localDocumentsView.getDocumentsMatchingQuery(query);
42+
return localDocumentsView.getDocumentsMatchingQuery(query, SnapshotVersion.NONE);
4043
}
4144

4245
@Override

firebase-firestore/src/test/java/com/google/firebase/firestore/local/IndexedQueryEngineTest.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ public void addDocumentQuery() {
213213
Query query = query("coll").filter(filter("a", "==", "a"));
214214

215215
ImmutableSortedMap<DocumentKey, Document> results =
216-
queryEngine.getDocumentsMatchingQuery(query);
216+
queryEngine.getDocumentsMatchingQuery(query, /* queryData= */ null);
217217

218218
assertThat(results).doesNotContain(IGNORED_DOC.getKey());
219219
assertThat(results).contains(MATCHING_DOC.getKey());
@@ -228,7 +228,7 @@ public void updateDocumentQuery() {
228228
Query query = query("coll").filter(filter("a", "==", "a"));
229229

230230
ImmutableSortedMap<DocumentKey, Document> results =
231-
queryEngine.getDocumentsMatchingQuery(query);
231+
queryEngine.getDocumentsMatchingQuery(query, /* queryData= */ null);
232232

233233
assertThat(results).doesNotContain(IGNORED_DOC.getKey());
234234
assertThat(results).contains(MATCHING_DOC.getKey());
@@ -243,7 +243,7 @@ public void removeDocumentQuery() {
243243
Query query = query("coll").filter(filter("a", "==", "a"));
244244

245245
ImmutableSortedMap<DocumentKey, Document> results =
246-
queryEngine.getDocumentsMatchingQuery(query);
246+
queryEngine.getDocumentsMatchingQuery(query, /* queryData= */ null);
247247

248248
assertThat(results).doesNotContain(IGNORED_DOC.getKey());
249249
assertThat(results).doesNotContain(MATCHING_DOC.getKey());
@@ -261,7 +261,7 @@ public void nestedQuery() {
261261
Query query = query("coll").filter(filter("a.a", "==", "a"));
262262

263263
ImmutableSortedMap<DocumentKey, Document> results =
264-
queryEngine.getDocumentsMatchingQuery(query);
264+
queryEngine.getDocumentsMatchingQuery(query, /* queryData= */ null);
265265

266266
assertThat(results).doesNotContain(ignoredDoc.getKey());
267267
assertThat(results).contains(matchingDoc.getKey());
@@ -275,7 +275,7 @@ public void orderByQuery() {
275275
Query query = query("coll").orderBy(TestUtil.orderBy("a"));
276276

277277
ImmutableSortedMap<DocumentKey, Document> results =
278-
queryEngine.getDocumentsMatchingQuery(query);
278+
queryEngine.getDocumentsMatchingQuery(query, /* queryData= */ null);
279279

280280
assertThat(results).doesNotContain(IGNORED_DOC.getKey());
281281
assertThat(results).contains(MATCHING_DOC.getKey());

firebase-firestore/src/test/java/com/google/firebase/firestore/local/RemoteDocumentCacheTestCase.java

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import static com.google.firebase.firestore.testutil.TestUtil.map;
2222
import static com.google.firebase.firestore.testutil.TestUtil.path;
2323
import static com.google.firebase.firestore.testutil.TestUtil.values;
24+
import static com.google.firebase.firestore.testutil.TestUtil.version;
2425
import static java.util.Arrays.asList;
2526
import static org.junit.Assert.assertEquals;
2627
import static org.junit.Assert.assertNotEquals;
@@ -32,6 +33,7 @@
3233
import com.google.firebase.firestore.model.DocumentKey;
3334
import com.google.firebase.firestore.model.MaybeDocument;
3435
import com.google.firebase.firestore.model.NoDocument;
36+
import com.google.firebase.firestore.model.SnapshotVersion;
3537
import java.util.ArrayList;
3638
import java.util.Arrays;
3739
import java.util.HashMap;
@@ -175,13 +177,31 @@ public void testDocumentsMatchingQuery() {
175177

176178
Query query = Query.atPath(path("b"));
177179
ImmutableSortedMap<DocumentKey, Document> results =
178-
remoteDocumentCache.getAllDocumentsMatchingQuery(query);
180+
remoteDocumentCache.getAllDocumentsMatchingQuery(query, SnapshotVersion.NONE);
179181
List<Document> expected = asList(doc("b/1", 42, docData), doc("b/2", 42, docData));
180182
assertEquals(expected, values(results));
181183
}
182184

185+
@Test
186+
public void testDocumentsMatchingQuerySinceUpdateTIme() {
187+
Map<String, Object> docData = map("data", 2);
188+
addTestDocumentAtPath("b/old", /* version= */ 41);
189+
addTestDocumentAtPath("b/current", /* version= */ 42);
190+
addTestDocumentAtPath("b/new", /* version= */ 43);
191+
192+
Query query = Query.atPath(path("b"));
193+
ImmutableSortedMap<DocumentKey, Document> results =
194+
remoteDocumentCache.getAllDocumentsMatchingQuery(query, version(42));
195+
List<Document> expected = asList(doc("b/new", 43, docData));
196+
assertEquals(expected, values(results));
197+
}
198+
183199
private Document addTestDocumentAtPath(String path) {
184-
Document doc = doc(path, 42, map("data", 2));
200+
return addTestDocumentAtPath(path, /* version= */ 42);
201+
}
202+
203+
private Document addTestDocumentAtPath(String path, int version) {
204+
Document doc = doc(path, version, map("data", 2));
185205
add(doc);
186206
return doc;
187207
}

0 commit comments

Comments
 (0)