diff --git a/google-cloud-clients/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CollectionReference.java b/google-cloud-clients/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CollectionReference.java
index fd41c86851c9..2167026a7504 100644
--- a/google-cloud-clients/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CollectionReference.java
+++ b/google-cloud-clients/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CollectionReference.java
@@ -19,7 +19,14 @@
import com.google.api.core.ApiFunction;
import com.google.api.core.ApiFuture;
import com.google.api.core.ApiFutures;
+import com.google.api.gax.rpc.ApiException;
+import com.google.api.gax.rpc.ApiExceptions;
+import com.google.cloud.firestore.v1beta1.FirestoreClient.ListDocumentsPagedResponse;
import com.google.common.base.Preconditions;
+import com.google.firestore.v1beta1.Document;
+import com.google.firestore.v1beta1.DocumentMask;
+import com.google.firestore.v1beta1.ListDocumentsRequest;
+import java.util.Iterator;
import java.util.Map;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@@ -104,6 +111,62 @@ public DocumentReference document(@Nonnull String childPath) {
return new DocumentReference(firestore, documentPath);
}
+ /**
+ * Retrieves the list of documents in this collection.
+ *
+ *
The document references returned may include references to "missing documents", i.e.
+ * document locations that have no document present but which contain subcollections with
+ * documents. Attempting to read such a document reference (e.g. via `get()` or `onSnapshot()`)
+ * will return a `DocumentSnapshot` whose `exists()` method returns false.
+ *
+ * @return {Promise} @return {Promise} The list of
+ * documents in this * collection.
+ */
+ @Nonnull
+ public Iterable listDocuments() {
+ ListDocumentsRequest.Builder request = ListDocumentsRequest.newBuilder();
+ request.setParent(path.getParent().toString());
+ request.setCollectionId(this.getId());
+ request.setMask(DocumentMask.getDefaultInstance());
+ request.setShowMissing(true);
+
+ final ListDocumentsPagedResponse response;
+
+ try {
+ response =
+ ApiExceptions.callAndTranslateApiException(
+ firestore.sendRequest(
+ request.build(), firestore.getClient().listDocumentsPagedCallable()));
+ } catch (ApiException exception) {
+ throw FirestoreException.apiException(exception);
+ }
+
+ return new Iterable() {
+ @Override
+ @Nonnull
+ public Iterator iterator() {
+ final Iterator iterator = response.iterateAll().iterator();
+ return new Iterator() {
+ @Override
+ public boolean hasNext() {
+ return iterator.hasNext();
+ }
+
+ @Override
+ public DocumentReference next() {
+ ResourcePath path = ResourcePath.create(iterator.next().getName());
+ return document(path.getId());
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException("remove");
+ }
+ };
+ }
+ };
+ }
+
/**
* Adds a new document to this collection with the specified data, assigning it a document ID
* automatically.
diff --git a/google-cloud-clients/google-cloud-firestore/src/main/java/com/google/cloud/firestore/spi/v1beta1/FirestoreRpc.java b/google-cloud-clients/google-cloud-firestore/src/main/java/com/google/cloud/firestore/spi/v1beta1/FirestoreRpc.java
index 3ffb242e1a06..24ba05008fd7 100644
--- a/google-cloud-clients/google-cloud-firestore/src/main/java/com/google/cloud/firestore/spi/v1beta1/FirestoreRpc.java
+++ b/google-cloud-clients/google-cloud-firestore/src/main/java/com/google/cloud/firestore/spi/v1beta1/FirestoreRpc.java
@@ -21,6 +21,7 @@
import com.google.api.gax.rpc.UnaryCallable;
import com.google.cloud.ServiceRpc;
import com.google.cloud.firestore.v1beta1.FirestoreClient.ListCollectionIdsPagedResponse;
+import com.google.cloud.firestore.v1beta1.FirestoreClient.ListDocumentsPagedResponse;
import com.google.firestore.v1beta1.BatchGetDocumentsRequest;
import com.google.firestore.v1beta1.BatchGetDocumentsResponse;
import com.google.firestore.v1beta1.BeginTransactionRequest;
@@ -28,13 +29,13 @@
import com.google.firestore.v1beta1.CommitRequest;
import com.google.firestore.v1beta1.CommitResponse;
import com.google.firestore.v1beta1.ListCollectionIdsRequest;
+import com.google.firestore.v1beta1.ListDocumentsRequest;
import com.google.firestore.v1beta1.ListenRequest;
import com.google.firestore.v1beta1.ListenResponse;
import com.google.firestore.v1beta1.RollbackRequest;
import com.google.firestore.v1beta1.RunQueryRequest;
import com.google.firestore.v1beta1.RunQueryResponse;
import com.google.protobuf.Empty;
-import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
/** Contains the RPC stubs used by the manual Cloud Firestore client. */
@@ -63,6 +64,9 @@ public interface FirestoreRpc extends AutoCloseable, ServiceRpc {
UnaryCallable
listCollectionIdsPagedCallable();
+ /** Returns a list of documents. */
+ UnaryCallable listDocumentsPagedCallable();
+
/** Returns a bi-directional watch stream. */
BidiStreamingCallable listenCallable();
}
diff --git a/google-cloud-clients/google-cloud-firestore/src/main/java/com/google/cloud/firestore/spi/v1beta1/GrpcFirestoreRpc.java b/google-cloud-clients/google-cloud-firestore/src/main/java/com/google/cloud/firestore/spi/v1beta1/GrpcFirestoreRpc.java
index 074105d41274..f9103cb59fab 100644
--- a/google-cloud-clients/google-cloud-firestore/src/main/java/com/google/cloud/firestore/spi/v1beta1/GrpcFirestoreRpc.java
+++ b/google-cloud-clients/google-cloud-firestore/src/main/java/com/google/cloud/firestore/spi/v1beta1/GrpcFirestoreRpc.java
@@ -33,10 +33,11 @@
import com.google.cloud.NoCredentials;
import com.google.cloud.ServiceOptions;
import com.google.cloud.firestore.FirestoreOptions;
-import com.google.cloud.firestore.v1beta1.FirestoreSettings;
-import com.google.cloud.firestore.v1beta1.stub.FirestoreStubSettings;
import com.google.cloud.firestore.v1beta1.FirestoreClient.ListCollectionIdsPagedResponse;
+import com.google.cloud.firestore.v1beta1.FirestoreClient.ListDocumentsPagedResponse;
+import com.google.cloud.firestore.v1beta1.FirestoreSettings;
import com.google.cloud.firestore.v1beta1.stub.FirestoreStub;
+import com.google.cloud.firestore.v1beta1.stub.FirestoreStubSettings;
import com.google.cloud.firestore.v1beta1.stub.GrpcFirestoreStub;
import com.google.cloud.grpc.GrpcTransportOptions;
import com.google.cloud.grpc.GrpcTransportOptions.ExecutorFactory;
@@ -48,6 +49,7 @@
import com.google.firestore.v1beta1.CommitResponse;
import com.google.firestore.v1beta1.DatabaseRootName;
import com.google.firestore.v1beta1.ListCollectionIdsRequest;
+import com.google.firestore.v1beta1.ListDocumentsRequest;
import com.google.firestore.v1beta1.ListenRequest;
import com.google.firestore.v1beta1.ListenResponse;
import com.google.firestore.v1beta1.RollbackRequest;
@@ -59,7 +61,6 @@
import io.grpc.ManagedChannelBuilder;
import java.io.IOException;
import java.util.Collections;
-import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
/**
@@ -129,7 +130,8 @@ public Void apply(UnaryCallSettings.Builder, ?> builder) {
}
};
FirestoreStubSettings.Builder firestoreBuilder =
- FirestoreStubSettings.newBuilder(clientContext).applyToAllUnaryMethods(retrySettingsSetter);
+ FirestoreStubSettings.newBuilder(clientContext)
+ .applyToAllUnaryMethods(retrySettingsSetter);
firestoreStub = GrpcFirestoreStub.create(firestoreBuilder.build());
} catch (Exception e) {
throw new IOException(e);
@@ -187,6 +189,12 @@ public UnaryCallable rollbackCallable() {
return firestoreStub.listCollectionIdsPagedCallable();
}
+ @Override
+ public UnaryCallable
+ listDocumentsPagedCallable() {
+ return firestoreStub.listDocumentsPagedCallable();
+ }
+
@Override
public BidiStreamingCallable listenCallable() {
return firestoreStub.listenCallable();
diff --git a/google-cloud-clients/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITSystemTest.java b/google-cloud-clients/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITSystemTest.java
index 55306fd8864d..f2d8080f0acc 100644
--- a/google-cloud-clients/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITSystemTest.java
+++ b/google-cloud-clients/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITSystemTest.java
@@ -577,6 +577,39 @@ public void getCollections() throws Exception {
assertEquals(collections.length, count);
}
+ @Test
+ public void listDocuments() throws Exception {
+ // We test with 21 documents since 20 documents are by default returned in a single paged
+ // response.
+ String[] documents =
+ new String[] {
+ "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16",
+ "17", "18", "19", "20", "21"
+ };
+ Arrays.sort(documents); // Sort in alphabetical (non-numeric) order.
+
+ WriteBatch batch = firestore.batch();
+ for (String document : documents) {
+ batch.create(randomColl.document(document), SINGLE_FIELD_OBJECT);
+ }
+ batch.commit().get();
+
+ Iterable collectionRefs = randomColl.listDocuments();
+
+ int count = 0;
+ for (DocumentReference documentRef : collectionRefs) {
+ assertEquals(documents[count++], documentRef.getId());
+ }
+ assertEquals(documents.length, count);
+ }
+
+ @Test
+ public void listDocumentsListsMissingDocument() throws Exception {
+ randomColl.document("missing/foo/bar").set(SINGLE_FIELD_MAP).get();
+ Iterable collectionRefs = randomColl.listDocuments();
+ assertEquals(randomColl.document("missing"), collectionRefs.iterator().next());
+ }
+
@Test
public void addAndRemoveFields() throws ExecutionException, InterruptedException {
Map expected = new HashMap<>();