Skip to content

Commit 4813432

Browse files
Add listDocuments() API (googleapis#3759)
1 parent 0f3bc0e commit 4813432

File tree

4 files changed

+113
-5
lines changed

4 files changed

+113
-5
lines changed

google-cloud-clients/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CollectionReference.java

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,14 @@
1919
import com.google.api.core.ApiFunction;
2020
import com.google.api.core.ApiFuture;
2121
import com.google.api.core.ApiFutures;
22+
import com.google.api.gax.rpc.ApiException;
23+
import com.google.api.gax.rpc.ApiExceptions;
24+
import com.google.cloud.firestore.v1beta1.FirestoreClient.ListDocumentsPagedResponse;
2225
import com.google.common.base.Preconditions;
26+
import com.google.firestore.v1beta1.Document;
27+
import com.google.firestore.v1beta1.DocumentMask;
28+
import com.google.firestore.v1beta1.ListDocumentsRequest;
29+
import java.util.Iterator;
2330
import java.util.Map;
2431
import javax.annotation.Nonnull;
2532
import javax.annotation.Nullable;
@@ -104,6 +111,62 @@ public DocumentReference document(@Nonnull String childPath) {
104111
return new DocumentReference(firestore, documentPath);
105112
}
106113

114+
/**
115+
* Retrieves the list of documents in this collection.
116+
*
117+
* <p>The document references returned may include references to "missing documents", i.e.
118+
* document locations that have no document present but which contain subcollections with
119+
* documents. Attempting to read such a document reference (e.g. via `get()` or `onSnapshot()`)
120+
* will return a `DocumentSnapshot` whose `exists()` method returns false.
121+
*
122+
* @return {Promise<DocumentReference[]>} @return {Promise<DocumentReference[]>} The list of
123+
* documents in this * collection.
124+
*/
125+
@Nonnull
126+
public Iterable<DocumentReference> listDocuments() {
127+
ListDocumentsRequest.Builder request = ListDocumentsRequest.newBuilder();
128+
request.setParent(path.getParent().toString());
129+
request.setCollectionId(this.getId());
130+
request.setMask(DocumentMask.getDefaultInstance());
131+
request.setShowMissing(true);
132+
133+
final ListDocumentsPagedResponse response;
134+
135+
try {
136+
response =
137+
ApiExceptions.callAndTranslateApiException(
138+
firestore.sendRequest(
139+
request.build(), firestore.getClient().listDocumentsPagedCallable()));
140+
} catch (ApiException exception) {
141+
throw FirestoreException.apiException(exception);
142+
}
143+
144+
return new Iterable<DocumentReference>() {
145+
@Override
146+
@Nonnull
147+
public Iterator<DocumentReference> iterator() {
148+
final Iterator<Document> iterator = response.iterateAll().iterator();
149+
return new Iterator<DocumentReference>() {
150+
@Override
151+
public boolean hasNext() {
152+
return iterator.hasNext();
153+
}
154+
155+
@Override
156+
public DocumentReference next() {
157+
ResourcePath path = ResourcePath.create(iterator.next().getName());
158+
return document(path.getId());
159+
}
160+
161+
@Override
162+
public void remove() {
163+
throw new UnsupportedOperationException("remove");
164+
}
165+
};
166+
}
167+
};
168+
}
169+
107170
/**
108171
* Adds a new document to this collection with the specified data, assigning it a document ID
109172
* automatically.

google-cloud-clients/google-cloud-firestore/src/main/java/com/google/cloud/firestore/spi/v1beta1/FirestoreRpc.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,20 +21,21 @@
2121
import com.google.api.gax.rpc.UnaryCallable;
2222
import com.google.cloud.ServiceRpc;
2323
import com.google.cloud.firestore.v1beta1.FirestoreClient.ListCollectionIdsPagedResponse;
24+
import com.google.cloud.firestore.v1beta1.FirestoreClient.ListDocumentsPagedResponse;
2425
import com.google.firestore.v1beta1.BatchGetDocumentsRequest;
2526
import com.google.firestore.v1beta1.BatchGetDocumentsResponse;
2627
import com.google.firestore.v1beta1.BeginTransactionRequest;
2728
import com.google.firestore.v1beta1.BeginTransactionResponse;
2829
import com.google.firestore.v1beta1.CommitRequest;
2930
import com.google.firestore.v1beta1.CommitResponse;
3031
import com.google.firestore.v1beta1.ListCollectionIdsRequest;
32+
import com.google.firestore.v1beta1.ListDocumentsRequest;
3133
import com.google.firestore.v1beta1.ListenRequest;
3234
import com.google.firestore.v1beta1.ListenResponse;
3335
import com.google.firestore.v1beta1.RollbackRequest;
3436
import com.google.firestore.v1beta1.RunQueryRequest;
3537
import com.google.firestore.v1beta1.RunQueryResponse;
3638
import com.google.protobuf.Empty;
37-
import java.util.concurrent.Executor;
3839
import java.util.concurrent.ScheduledExecutorService;
3940

4041
/** Contains the RPC stubs used by the manual Cloud Firestore client. */
@@ -63,6 +64,9 @@ public interface FirestoreRpc extends AutoCloseable, ServiceRpc {
6364
UnaryCallable<ListCollectionIdsRequest, ListCollectionIdsPagedResponse>
6465
listCollectionIdsPagedCallable();
6566

67+
/** Returns a list of documents. */
68+
UnaryCallable<ListDocumentsRequest, ListDocumentsPagedResponse> listDocumentsPagedCallable();
69+
6670
/** Returns a bi-directional watch stream. */
6771
BidiStreamingCallable<ListenRequest, ListenResponse> listenCallable();
6872
}

google-cloud-clients/google-cloud-firestore/src/main/java/com/google/cloud/firestore/spi/v1beta1/GrpcFirestoreRpc.java

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,11 @@
3333
import com.google.cloud.NoCredentials;
3434
import com.google.cloud.ServiceOptions;
3535
import com.google.cloud.firestore.FirestoreOptions;
36-
import com.google.cloud.firestore.v1beta1.FirestoreSettings;
37-
import com.google.cloud.firestore.v1beta1.stub.FirestoreStubSettings;
3836
import com.google.cloud.firestore.v1beta1.FirestoreClient.ListCollectionIdsPagedResponse;
37+
import com.google.cloud.firestore.v1beta1.FirestoreClient.ListDocumentsPagedResponse;
38+
import com.google.cloud.firestore.v1beta1.FirestoreSettings;
3939
import com.google.cloud.firestore.v1beta1.stub.FirestoreStub;
40+
import com.google.cloud.firestore.v1beta1.stub.FirestoreStubSettings;
4041
import com.google.cloud.firestore.v1beta1.stub.GrpcFirestoreStub;
4142
import com.google.cloud.grpc.GrpcTransportOptions;
4243
import com.google.cloud.grpc.GrpcTransportOptions.ExecutorFactory;
@@ -48,6 +49,7 @@
4849
import com.google.firestore.v1beta1.CommitResponse;
4950
import com.google.firestore.v1beta1.DatabaseRootName;
5051
import com.google.firestore.v1beta1.ListCollectionIdsRequest;
52+
import com.google.firestore.v1beta1.ListDocumentsRequest;
5153
import com.google.firestore.v1beta1.ListenRequest;
5254
import com.google.firestore.v1beta1.ListenResponse;
5355
import com.google.firestore.v1beta1.RollbackRequest;
@@ -59,7 +61,6 @@
5961
import io.grpc.ManagedChannelBuilder;
6062
import java.io.IOException;
6163
import java.util.Collections;
62-
import java.util.concurrent.Executor;
6364
import java.util.concurrent.ScheduledExecutorService;
6465

6566
/**
@@ -129,7 +130,8 @@ public Void apply(UnaryCallSettings.Builder<?, ?> builder) {
129130
}
130131
};
131132
FirestoreStubSettings.Builder firestoreBuilder =
132-
FirestoreStubSettings.newBuilder(clientContext).applyToAllUnaryMethods(retrySettingsSetter);
133+
FirestoreStubSettings.newBuilder(clientContext)
134+
.applyToAllUnaryMethods(retrySettingsSetter);
133135
firestoreStub = GrpcFirestoreStub.create(firestoreBuilder.build());
134136
} catch (Exception e) {
135137
throw new IOException(e);
@@ -187,6 +189,12 @@ public UnaryCallable<RollbackRequest, Empty> rollbackCallable() {
187189
return firestoreStub.listCollectionIdsPagedCallable();
188190
}
189191

192+
@Override
193+
public UnaryCallable<ListDocumentsRequest, ListDocumentsPagedResponse>
194+
listDocumentsPagedCallable() {
195+
return firestoreStub.listDocumentsPagedCallable();
196+
}
197+
190198
@Override
191199
public BidiStreamingCallable<ListenRequest, ListenResponse> listenCallable() {
192200
return firestoreStub.listenCallable();

google-cloud-clients/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITSystemTest.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -577,6 +577,39 @@ public void listCollections() throws Exception {
577577
assertEquals(collections.length, count);
578578
}
579579

580+
@Test
581+
public void listDocuments() throws Exception {
582+
// We test with 21 documents since 20 documents are by default returned in a single paged
583+
// response.
584+
String[] documents =
585+
new String[] {
586+
"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16",
587+
"17", "18", "19", "20", "21"
588+
};
589+
Arrays.sort(documents); // Sort in alphabetical (non-numeric) order.
590+
591+
WriteBatch batch = firestore.batch();
592+
for (String document : documents) {
593+
batch.create(randomColl.document(document), SINGLE_FIELD_OBJECT);
594+
}
595+
batch.commit().get();
596+
597+
Iterable<DocumentReference> collectionRefs = randomColl.listDocuments();
598+
599+
int count = 0;
600+
for (DocumentReference documentRef : collectionRefs) {
601+
assertEquals(documents[count++], documentRef.getId());
602+
}
603+
assertEquals(documents.length, count);
604+
}
605+
606+
@Test
607+
public void listDocumentsListsMissingDocument() throws Exception {
608+
randomColl.document("missing/foo/bar").set(SINGLE_FIELD_MAP).get();
609+
Iterable<DocumentReference> collectionRefs = randomColl.listDocuments();
610+
assertEquals(randomColl.document("missing"), collectionRefs.iterator().next());
611+
}
612+
580613
@Test
581614
public void addAndRemoveFields() throws ExecutionException, InterruptedException {
582615
Map<String, Object> expected = new HashMap<>();

0 commit comments

Comments
 (0)