Skip to content

Commit

Permalink
Add listDocuments() API (#3759)
Browse files Browse the repository at this point in the history
  • Loading branch information
schmidt-sebastian authored Oct 4, 2018
1 parent 0f3bc0e commit 4813432
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -104,6 +111,62 @@ public DocumentReference document(@Nonnull String childPath) {
return new DocumentReference(firestore, documentPath);
}

/**
* Retrieves the list of documents in this collection.
*
* <p>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<DocumentReference[]>} @return {Promise<DocumentReference[]>} The list of
* documents in this * collection.
*/
@Nonnull
public Iterable<DocumentReference> 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<DocumentReference>() {
@Override
@Nonnull
public Iterator<DocumentReference> iterator() {
final Iterator<Document> iterator = response.iterateAll().iterator();
return new Iterator<DocumentReference>() {
@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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,21 @@
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;
import com.google.firestore.v1beta1.BeginTransactionResponse;
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. */
Expand Down Expand Up @@ -63,6 +64,9 @@ public interface FirestoreRpc extends AutoCloseable, ServiceRpc {
UnaryCallable<ListCollectionIdsRequest, ListCollectionIdsPagedResponse>
listCollectionIdsPagedCallable();

/** Returns a list of documents. */
UnaryCallable<ListDocumentsRequest, ListDocumentsPagedResponse> listDocumentsPagedCallable();

/** Returns a bi-directional watch stream. */
BidiStreamingCallable<ListenRequest, ListenResponse> listenCallable();
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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;

/**
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -187,6 +189,12 @@ public UnaryCallable<RollbackRequest, Empty> rollbackCallable() {
return firestoreStub.listCollectionIdsPagedCallable();
}

@Override
public UnaryCallable<ListDocumentsRequest, ListDocumentsPagedResponse>
listDocumentsPagedCallable() {
return firestoreStub.listDocumentsPagedCallable();
}

@Override
public BidiStreamingCallable<ListenRequest, ListenResponse> listenCallable() {
return firestoreStub.listenCallable();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -577,6 +577,39 @@ public void listCollections() 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<DocumentReference> 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<DocumentReference> collectionRefs = randomColl.listDocuments();
assertEquals(randomColl.document("missing"), collectionRefs.iterator().next());
}

@Test
public void addAndRemoveFields() throws ExecutionException, InterruptedException {
Map<String, Object> expected = new HashMap<>();
Expand Down

0 comments on commit 4813432

Please sign in to comment.