Skip to content

Commit 275b3eb

Browse files
Add getOverlays(CollectionGroup) (#3330)
1 parent 20fe010 commit 275b3eb

File tree

4 files changed

+129
-0
lines changed

4 files changed

+129
-0
lines changed

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

+14
Original file line numberDiff line numberDiff line change
@@ -56,4 +56,18 @@ public interface DocumentOverlayCache {
5656
* @return Mapping of each document key in the collection to its overlay.
5757
*/
5858
Map<DocumentKey, Overlay> getOverlays(ResourcePath collection, int sinceBatchId);
59+
60+
/**
61+
* Returns {@code count} overlays with a batch ID higher than {@code sinceBatchId} for the
62+
* provided collection group, processed by ascending batch ID. The method always returns all
63+
* overlays for a batch even if the last batch contains more documents than the remaining limit.
64+
*
65+
* @param collectionGroup The collection group to get the overlays for.
66+
* @param sinceBatchId The minimum batch ID to filter by (exclusive). Only overlays that contain a
67+
* change past `sinceBatchId` are returned.
68+
* @param count The number of overlays to return. Can be exceeded if the last batch contains more
69+
* entries.
70+
* @return Mapping of each document key in the collection group to its overlay.
71+
*/
72+
Map<DocumentKey, Overlay> getOverlays(String collectionGroup, int sinceBatchId, int count);
5973
}

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

+32
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import java.util.HashSet;
2424
import java.util.Map;
2525
import java.util.Set;
26+
import java.util.SortedMap;
2627
import java.util.TreeMap;
2728

2829
public class MemoryDocumentOverlayCache implements DocumentOverlayCache {
@@ -100,4 +101,35 @@ public Map<DocumentKey, Overlay> getOverlays(ResourcePath collection, int sinceB
100101

101102
return result;
102103
}
104+
105+
@Override
106+
public Map<DocumentKey, Overlay> getOverlays(
107+
String collectionGroup, int sinceBatchId, int count) {
108+
SortedMap<Integer, Map<DocumentKey, Overlay>> batchIdToOverlays = new TreeMap<>();
109+
110+
for (Overlay overlay : overlays.values()) {
111+
DocumentKey key = overlay.getKey();
112+
if (!key.getCollectionGroup().equals(collectionGroup)) {
113+
continue;
114+
}
115+
if (overlay.getLargestBatchId() > sinceBatchId) {
116+
Map<DocumentKey, Overlay> overlays = batchIdToOverlays.get(overlay.getLargestBatchId());
117+
if (overlays == null) {
118+
overlays = new HashMap<>();
119+
batchIdToOverlays.put(overlay.getLargestBatchId(), overlays);
120+
}
121+
overlays.put(overlay.getKey(), overlay);
122+
}
123+
}
124+
125+
Map<DocumentKey, Overlay> result = new HashMap<>();
126+
for (Map<DocumentKey, Overlay> overlays : batchIdToOverlays.values()) {
127+
result.putAll(overlays);
128+
if (result.size() >= count) {
129+
break;
130+
}
131+
}
132+
133+
return result;
134+
}
103135
}

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

+45
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,51 @@ public Map<DocumentKey, Overlay> getOverlays(ResourcePath collection, int sinceB
9999
return result;
100100
}
101101

102+
@Override
103+
public Map<DocumentKey, Overlay> getOverlays(
104+
String collectionGroup, int sinceBatchId, int count) {
105+
Map<DocumentKey, Overlay> result = new HashMap<>();
106+
Overlay[] lastOverlay = new Overlay[] {null};
107+
108+
db.query(
109+
"SELECT overlay_mutation, largest_batch_id FROM document_overlays "
110+
+ "WHERE uid = ? AND collection_group = ? AND largest_batch_id > ? "
111+
+ "ORDER BY largest_batch_id, collection_path, document_id LIMIT ?")
112+
.binding(uid, collectionGroup, sinceBatchId, count)
113+
.forEach(
114+
row -> {
115+
lastOverlay[0] = decodeOverlay(row);
116+
result.put(lastOverlay[0].getKey(), lastOverlay[0]);
117+
});
118+
119+
if (lastOverlay[0] == null) {
120+
return result;
121+
}
122+
123+
// Finish batch
124+
DocumentKey key = lastOverlay[0].getKey();
125+
String encodedCollectionPath = EncodedPath.encode(key.getCollectionPath());
126+
db.query(
127+
"SELECT overlay_mutation, largest_batch_id FROM document_overlays "
128+
+ "WHERE uid = ? AND collection_group = ? "
129+
+ "AND (collection_path > ? OR (collection_path = ? AND document_id > ?)) "
130+
+ "AND largest_batch_id = ?")
131+
.binding(
132+
uid,
133+
collectionGroup,
134+
encodedCollectionPath,
135+
encodedCollectionPath,
136+
key.getDocumentId(),
137+
lastOverlay[0].getLargestBatchId())
138+
.forEach(
139+
row -> {
140+
Overlay overlay = decodeOverlay(row);
141+
result.put(overlay.getKey(), overlay);
142+
});
143+
144+
return result;
145+
}
146+
102147
private Overlay decodeOverlay(android.database.Cursor row) {
103148
try {
104149
Write write = Write.parseFrom(row.getBlob(0));

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

+38
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,44 @@ public void testGetAllOverlaysSinceBatchId() {
153153
verifyOverlayContains(overlays, "coll/doc3", "coll/doc4");
154154
}
155155

156+
@Test
157+
public void testGetAllOverlaysFromCollectionGroupEnforcesCollectionGroup() {
158+
saveOverlays(2, "coll1/doc1", "coll2/doc1");
159+
saveOverlays(3, "coll1/doc2");
160+
saveOverlays(4, "coll2/doc2");
161+
162+
Map<DocumentKey, Overlay> overlays = cache.getOverlays("coll1", -1, 50);
163+
verifyOverlayContains(overlays, "coll1/doc1", "coll1/doc2");
164+
}
165+
166+
@Test
167+
public void testGetAllOverlaysFromCollectionGroupEnforcesBatchId() {
168+
saveOverlays(2, "coll/doc1");
169+
saveOverlays(3, "coll/doc2");
170+
171+
Map<DocumentKey, Overlay> overlays = cache.getOverlays("coll", 2, 50);
172+
verifyOverlayContains(overlays, "coll/doc2");
173+
}
174+
175+
@Test
176+
public void testGetAllOverlaysFromCollectionGroupEnforcesLimit() {
177+
saveOverlays(1, "coll/doc1");
178+
saveOverlays(2, "coll/doc2");
179+
saveOverlays(3, "coll/doc3");
180+
181+
Map<DocumentKey, Overlay> overlays = cache.getOverlays("coll", -1, 2);
182+
verifyOverlayContains(overlays, "coll/doc1", "coll/doc2");
183+
}
184+
185+
@Test
186+
public void testGetAllOverlaysFromCollectionGroupWithLimitIncludesFullBatches() {
187+
saveOverlays(1, "coll/doc1");
188+
saveOverlays(2, "coll/doc2", "coll/doc3");
189+
190+
Map<DocumentKey, Overlay> overlays = cache.getOverlays("coll", -1, 2);
191+
verifyOverlayContains(overlays, "coll/doc1", "coll/doc2", "coll/doc3");
192+
}
193+
156194
void verifyOverlayContains(Map<DocumentKey, Overlay> overlays, String... keys) {
157195
Set<DocumentKey> expected = Arrays.stream(keys).map(TestUtil::key).collect(Collectors.toSet());
158196
assertThat(overlays.keySet()).containsExactlyElementsIn(expected);

0 commit comments

Comments
 (0)