Skip to content

Commit 4acf78d

Browse files
Implementing Firestore Watch
1 parent e200f32 commit 4acf78d

13 files changed

Lines changed: 1350 additions & 274 deletions

File tree

google-cloud-firestore/src/main/java/com/google/cloud/firestore/DocumentChange.java

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,6 @@
2121
/**
2222
* A DocumentChange represents a change to the documents matching a query. It contains the document
2323
* affected and a the type of change that occurred (added, modifed, or removed).
24-
*
25-
* <p><b>Subclassing Note</b>: Firestore classes are not meant to be subclassed except for use in
26-
* test mocks. Subclassing is not supported in production code and new SDK releases may break code
27-
* that does so.
2824
*/
2925
public class DocumentChange {
3026
/** An enumeration of snapshot diff types. */

google-cloud-firestore/src/main/java/com/google/cloud/firestore/DocumentReference.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -412,7 +412,6 @@ public void onEvent(
412412
if (value != null) {
413413
for (DocumentSnapshot doc : value) {
414414
if (doc.getReference().equals(DocumentReference.this)) {
415-
416415
listener.onEvent(value.getDocuments().get(0), null);
417416
return;
418417
}
@@ -437,7 +436,7 @@ public void onEvent(
437436
@Nonnull
438437
public ListenerRegistration addSnapshotListener(
439438
@Nonnull EventListener<DocumentSnapshot> listener) {
440-
return addSnapshotListener(null, listener);
439+
return addSnapshotListener(firestore.getClient().getExecutor(), listener);
441440
}
442441

443442
ResourcePath getResourcePath() {

google-cloud-firestore/src/main/java/com/google/cloud/firestore/DocumentSet.java

Lines changed: 10 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,11 @@
1414
* by default if no document is present.
1515
*/
1616
class DocumentSet implements Iterable<DocumentSnapshot> {
17-
// Since immutable maps are covariant in the value type, we don't care about the value type
1817
private static final ImmutableSortedMap<ResourcePath, DocumentSnapshot> EMPTY_DOCUMENT_MAP =
1918
ImmutableSortedMap.Builder.emptyMap(ResourcePath.comparator());
2019

2120
/** Returns an empty DocumentSet sorted by the given comparator, then by keys. */
22-
public static DocumentSet emptySet(final Comparator<DocumentSnapshot> comparator) {
21+
static DocumentSet emptySet(final Comparator<DocumentSnapshot> comparator) {
2322
return new DocumentSet(
2423
EMPTY_DOCUMENT_MAP,
2524
new ImmutableSortedSet<>(Collections.<DocumentSnapshot>emptyList(), comparator));
@@ -46,65 +45,30 @@ private DocumentSet(
4645
this.sortedSet = sortedSet;
4746
}
4847

49-
public int size() {
48+
int size() {
5049
return keyIndex.size();
5150
}
5251

53-
public boolean isEmpty() {
52+
boolean isEmpty() {
5453
return keyIndex.isEmpty();
5554
}
5655

5756
/** Returns true iff this set contains a document with the given key. */
58-
public boolean contains(ResourcePath key) {
57+
boolean contains(ResourcePath key) {
5958
return keyIndex.containsKey(key);
6059
}
6160

6261
/** Returns the document from this set with the given key if it exists or null if it doesn't. */
6362
@Nullable
64-
public DocumentSnapshot getDocument(ResourcePath key) {
63+
DocumentSnapshot getDocument(ResourcePath key) {
6564
return keyIndex.get(key);
6665
}
6766

68-
/**
69-
* Returns the first document in the set according to the set's ordering, or null if the set is
70-
* empty.
71-
*/
72-
@Nullable
73-
public DocumentSnapshot getFirstDocument() {
74-
return sortedSet.getMinEntry();
75-
}
76-
77-
/**
78-
* Returns the last document in the set according to the set's ordering, or null if the set is
79-
* empty.
80-
*/
81-
@Nullable
82-
public DocumentSnapshot getLastDocument() {
83-
return sortedSet.getMaxEntry();
84-
}
85-
86-
/**
87-
* Returns the document previous to the document associated with the given key in the set
88-
* according to the set's ordering. Returns null if the document associated with the given key is
89-
* the first document.
90-
*
91-
* @param key A key that must be present in the set
92-
* @throws IllegalArgumentException if the set does not contain the key
93-
*/
94-
@Nullable
95-
public DocumentSnapshot getPredecessor(ResourcePath key) {
96-
DocumentSnapshot document = keyIndex.get(key);
97-
if (document == null) {
98-
throw new IllegalArgumentException("Key not contained in DocumentSet: " + key);
99-
}
100-
return sortedSet.getPredecessorEntry(document);
101-
}
102-
10367
/**
10468
* Returns the index of the provided key in the document set, or -1 if the document key is not
10569
* present in the set;
10670
*/
107-
public int indexOf(ResourcePath key) {
71+
int indexOf(ResourcePath key) {
10872
DocumentSnapshot document = keyIndex.get(key);
10973
if (document == null) {
11074
return -1;
@@ -116,7 +80,7 @@ public int indexOf(ResourcePath key) {
11680
* Returns a new DocumentSet that contains the given document, replacing any old document with the
11781
* same key.
11882
*/
119-
public DocumentSet add(DocumentSnapshot document) {
83+
DocumentSet add(DocumentSnapshot document) {
12084
// Remove any prior mapping of the document's key before adding, preventing sortedSet from
12185
// accumulating values that aren't in the index.
12286
DocumentSet removed = remove(document.getReference().getResourcePath());
@@ -128,7 +92,7 @@ public DocumentSet add(DocumentSnapshot document) {
12892
}
12993

13094
/** Returns a new DocumentSet with the document for the provided key removed. */
131-
public DocumentSet remove(ResourcePath key) {
95+
DocumentSet remove(ResourcePath key) {
13296
DocumentSnapshot document = keyIndex.get(key);
13397
if (document == null) {
13498
return this;
@@ -140,10 +104,9 @@ public DocumentSet remove(ResourcePath key) {
140104
}
141105

142106
/**
143-
* Returns a copy of the documents in this set as array. This is O(n) in the size of the set
144-
* TODO(dimond): Consider making this backed by the set instead to achieve O(1)?
107+
* Returns a copy of the documents in this set as array. This is O(n) in the size of the set.
145108
*/
146-
public List<DocumentSnapshot> toList() {
109+
List<DocumentSnapshot> toList() {
147110
List<DocumentSnapshot> documents = new ArrayList<>(size());
148111
for (DocumentSnapshot document : this) {
149112
documents.add(document);
@@ -155,7 +118,6 @@ public List<DocumentSnapshot> toList() {
155118
public Iterator<DocumentSnapshot> iterator() {
156119
return sortedSet.iterator();
157120
}
158-
159121
@Override
160122
public boolean equals(Object other) {
161123
if (this == other) {

google-cloud-firestore/src/main/java/com/google/cloud/firestore/ExponentialBackoff.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,17 +25,17 @@ class ExponentialBackoff {
2525
* The initial backoff time in milliseconds after an error.
2626
* Set to 1s according to https://cloud.google.com/apis/design/errors.
2727
*/
28-
private int INITIAL_DELAY_MS = 1000;
28+
private static int INITIAL_DELAY_MS = 1000;
2929

3030
/*!
3131
* The maximum backoff time in milliseconds.
3232
*/
33-
private int MAX_DELAY_MS = 60 * 1000;
33+
private static int MAX_DELAY_MS = 60 * 1000;
3434

3535
/*!
3636
* The factor to increase the backup by after each failed attempt.
3737
*/
38-
private double BACKOFF_FACTOR = 1.5;
38+
private static double BACKOFF_FACTOR = 1.5;
3939

4040
private final ScheduledExecutorService executor;
4141
private final long initialDelayMs;

google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreException.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,19 @@
1818

1919
import com.google.api.gax.rpc.ApiException;
2020
import com.google.cloud.grpc.BaseGrpcServiceException;
21+
import com.google.common.annotations.VisibleForTesting;
2122
import io.grpc.Status;
2223
import java.io.IOException;
24+
import javax.annotation.Nullable;
2325

2426
/** A Firestore Service exception. */
2527
public final class FirestoreException extends BaseGrpcServiceException {
28+
private Status status;
2629

2730
private FirestoreException(String reason, Status status) {
2831
super(reason, null, status.getCode().value(), false);
32+
33+
this.status = status;
2934
}
3035

3136
private FirestoreException(IOException exception, boolean retryable) {
@@ -73,4 +78,10 @@ static FirestoreException networkException(IOException exception, boolean retrya
7378
static FirestoreException apiException(ApiException exception) {
7479
return new FirestoreException(exception);
7580
}
81+
82+
@VisibleForTesting
83+
@Nullable
84+
Status getStatus() {
85+
return status;
86+
}
7687
}

google-cloud-firestore/src/main/java/com/google/cloud/firestore/Query.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -763,7 +763,7 @@ public ApiFuture<QuerySnapshot> get() {
763763
*/
764764
@Nonnull
765765
public ListenerRegistration addSnapshotListener(@Nonnull EventListener<QuerySnapshot> listener) {
766-
return addSnapshotListener(null, listener);
766+
return addSnapshotListener(firestore.getClient().getExecutor(), listener);
767767
}
768768

769769
/**

google-cloud-firestore/src/main/java/com/google/cloud/firestore/QuerySnapshot.java

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@
1717
package com.google.cloud.firestore;
1818

1919
import com.google.common.base.Preconditions;
20-
import com.google.common.collect.ImmutableList;
2120
import java.util.ArrayList;
21+
import java.util.Collections;
2222
import java.util.Iterator;
2323
import java.util.List;
2424
import java.util.Objects;
@@ -33,7 +33,7 @@ public final class QuerySnapshot implements Iterable<DocumentSnapshot> {
3333

3434
private final Query query;
3535
private final DocumentSet documentSet;
36-
private List<DocumentSnapshot> documentSnapshots;
36+
private volatile List<DocumentSnapshot> documentSnapshots;
3737
private final Instant readTime;
3838
private final List<DocumentChange> documentChanges;
3939

@@ -87,11 +87,13 @@ public List<DocumentSnapshot> getDocuments() {
8787
if (documentSnapshots == null) {
8888
Preconditions.checkState(documentSet != null);
8989
synchronized (documentSet) {
90-
documentSnapshots = documentSet.toList();
90+
if (documentSnapshots == null) {
91+
documentSnapshots = documentSet.toList();
92+
}
9193
}
9294
}
9395

94-
return ImmutableList.copyOf(documentSnapshots);
96+
return Collections.unmodifiableList(documentSnapshots);
9597
}
9698

9799
/**
@@ -102,17 +104,17 @@ public List<DocumentSnapshot> getDocuments() {
102104
*/
103105
@Nonnull
104106
public List<DocumentChange> getDocumentChanges() {
105-
return ImmutableList.copyOf(documentChanges);
107+
return Collections.unmodifiableList(documentChanges);
106108
}
107109

108110
/** Returns true if there are no documents in the QuerySnapshot. */
109111
public boolean isEmpty() {
110-
return documentSnapshots.isEmpty();
112+
return documentSnapshots != null ? documentSnapshots.isEmpty() : documentSet.isEmpty();
111113
}
112114

113115
/** Returns the number of documents in the QuerySnapshot. */
114116
public int size() {
115-
return documentSnapshots.size();
117+
return documentSnapshots != null ? documentSnapshots.size() : documentSet.size();
116118
}
117119

118120
@Override

google-cloud-firestore/src/main/java/com/google/cloud/firestore/ResourcePath.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -143,16 +143,16 @@ String getName() {
143143
*/
144144
@Override
145145
public int compareTo(@Nonnull ResourcePath other) {
146-
int cmp = this.getDatabaseName().getProject().compareTo(other.getDatabaseName().getProject());
146+
int comp = this.getDatabaseName().getProject().compareTo(other.getDatabaseName().getProject());
147147

148-
if (cmp != 0) {
149-
return Integer.compare(cmp, 0);
148+
if (comp != 0) {
149+
return Integer.compare(comp, 0);
150150
}
151151

152-
cmp = this.getDatabaseName().getDatabase().compareTo(other.getDatabaseName().getDatabase());
152+
comp = this.getDatabaseName().getDatabase().compareTo(other.getDatabaseName().getDatabase());
153153

154-
if (cmp != 0) {
155-
return Integer.compare(cmp, 0);
154+
if (comp != 0) {
155+
return Integer.compare(comp, 0);
156156
}
157157

158158
return super.compareTo(other);

0 commit comments

Comments
 (0)